diff --git a/src/apis/avm/api.ts b/src/apis/avm/api.ts index 6ba2e27bf..81d3ecd7c 100644 --- a/src/apis/avm/api.ts +++ b/src/apis/avm/api.ts @@ -1017,7 +1017,8 @@ export class AVMAPI extends JRPCAPI { * @param memo Optional CB58 Buffer or String which contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param locktime Optional. The locktime field created in the resulting outputs - * @param threshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param toThreshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction ([[UnsignedTx]]) which contains a [[BaseTx]]. * @@ -1034,7 +1035,8 @@ export class AVMAPI extends JRPCAPI { memo: PayloadBase | Buffer = undefined, asOf: BN = UnixNow(), locktime: BN = new BN(0), - threshold: number = 1 + toThreshold: number = 1, + changeThreshold: number = 1 ): Promise => { const caller: string = "buildBaseTx" const to: Buffer[] = this._cleanAddressArray(toAddresses, caller).map( @@ -1073,7 +1075,8 @@ export class AVMAPI extends JRPCAPI { memo, asOf, locktime, - threshold + toThreshold, + changeThreshold ) if (!(await this.checkGooseEgg(builtUnsignedTx))) { @@ -1098,7 +1101,8 @@ export class AVMAPI extends JRPCAPI { * @param memo Optional CB58 Buffer or String which contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param locktime Optional. The locktime field created in the resulting outputs - * @param threshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param toThreshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction ([[UnsignedTx]]) which contains a [[NFTTransferTx]]. * @@ -1114,7 +1118,8 @@ export class AVMAPI extends JRPCAPI { memo: PayloadBase | Buffer = undefined, asOf: BN = UnixNow(), locktime: BN = new BN(0), - threshold: number = 1 + toThreshold: number = 1, + changeThreshold: number = 1 ): Promise => { const caller: string = "buildNFTTransferTx" const to: Buffer[] = this._cleanAddressArray(toAddresses, caller).map( @@ -1152,7 +1157,8 @@ export class AVMAPI extends JRPCAPI { memo, asOf, locktime, - threshold + toThreshold, + changeThreshold ) if (!(await this.checkGooseEgg(builtUnsignedTx))) { @@ -1178,7 +1184,8 @@ export class AVMAPI extends JRPCAPI { * @param memo Optional CB58 Buffer or String which contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param locktime Optional. The locktime field created in the resulting outputs - * @param threshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param toThreshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction ([[UnsignedTx]]) which contains a [[ImportTx]]. * @@ -1195,7 +1202,8 @@ export class AVMAPI extends JRPCAPI { memo: PayloadBase | Buffer = undefined, asOf: BN = UnixNow(), locktime: BN = new BN(0), - threshold: number = 1 + toThreshold: number = 1, + changeThreshold: number = 1 ): Promise => { const caller: string = "buildImportTx" const to: Buffer[] = this._cleanAddressArray(toAddresses, caller).map( @@ -1257,7 +1265,8 @@ export class AVMAPI extends JRPCAPI { memo, asOf, locktime, - threshold + toThreshold, + changeThreshold ) if (!(await this.checkGooseEgg(builtUnsignedTx))) { @@ -1283,7 +1292,8 @@ export class AVMAPI extends JRPCAPI { * @param memo Optional CB58 Buffer or String which contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param locktime Optional. The locktime field created in the resulting outputs - * @param threshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param toThreshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * @param assetID Optional. The assetID of the asset to send. Defaults to AVAX assetID. * Regardless of the asset which you"re exporting, all fees are paid in AVAX. * @@ -1299,7 +1309,8 @@ export class AVMAPI extends JRPCAPI { memo: PayloadBase | Buffer = undefined, asOf: BN = UnixNow(), locktime: BN = new BN(0), - threshold: number = 1, + toThreshold: number = 1, + changeThreshold: number = 1, assetID: string = undefined ): Promise => { const prefixes: object = {} @@ -1372,7 +1383,8 @@ export class AVMAPI extends JRPCAPI { memo, asOf, locktime, - threshold + toThreshold, + changeThreshold ) if (!(await this.checkGooseEgg(builtUnsignedTx))) { @@ -1399,6 +1411,7 @@ export class AVMAPI extends JRPCAPI { * @param mintOutputs Optional. Array of [[SECPMintOutput]]s to be included in the transaction. These outputs can be spent to mint more tokens. * @param memo Optional CB58 Buffer or String which contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction ([[UnsignedTx]]) which contains a [[CreateAssetTx]]. * @@ -1413,7 +1426,8 @@ export class AVMAPI extends JRPCAPI { denomination: number, mintOutputs: SECPMintOutput[] = undefined, memo: PayloadBase | Buffer = undefined, - asOf: BN = UnixNow() + asOf: BN = UnixNow(), + changeThreshold: number = 1 ): Promise => { const caller: string = "buildCreateAssetTx" const from: Buffer[] = this._cleanAddressArray(fromAddresses, caller).map( @@ -1458,7 +1472,8 @@ export class AVMAPI extends JRPCAPI { fee, avaxAssetID, memo, - asOf + asOf, + changeThreshold ) if (!(await this.checkGooseEgg(builtUnsignedTx, fee))) { @@ -1471,6 +1486,22 @@ export class AVMAPI extends JRPCAPI { return builtUnsignedTx } + /** + * Creates an unsigned Secp mint transaction. For more granular control, you may create your own + * [[OperationTx]] manually (with their corresponding [[TransferableInput]]s, [[TransferableOutput]]s, and [[TransferOperation]]s). + * + * @param utxoset A set of UTXOs that the transaction is built on + * @param mintOwner A [[SECPMintOutput]] which specifies the new set of minters + * @param transferOwner A [[SECPTransferOutput]] which specifies where the minted tokens will go + * @param fromAddresses The addresses being used to send the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} + * @param changeAddresses The addresses that can spend the change remaining from the spent UTXOs + * @param mintUTXOID The UTXOID for the [[SCPMintOutput]] being spent to produce more tokens + * @param memo Optional contains arbitrary bytes, up to 256 bytes + * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} + * @param changethreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO + * + * @returns An unsigned transaction ([[UnsignedTx]]) which contains a [[SECPMintTx]]. + */ buildSECPMintTx = async ( utxoset: UTXOSet, mintOwner: SECPMintOutput, @@ -1479,7 +1510,8 @@ export class AVMAPI extends JRPCAPI { changeAddresses: string[], mintUTXOID: string, memo: PayloadBase | Buffer = undefined, - asOf: BN = UnixNow() + asOf: BN = UnixNow(), + changeThreshold: number = 1 ): Promise => { const caller: string = "buildSECPMintTx" const from: Buffer[] = this._cleanAddressArray(fromAddresses, caller).map( @@ -1509,7 +1541,8 @@ export class AVMAPI extends JRPCAPI { fee, avaxAssetID, memo, - asOf + asOf, + changeThreshold ) if (!(await this.checkGooseEgg(builtUnsignedTx))) { /* istanbul ignore next */ @@ -1533,6 +1566,7 @@ export class AVMAPI extends JRPCAPI { * @param memo Optional CB58 Buffer or String which contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param locktime Optional. The locktime field created in the resulting mint output + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * ```js * Example minterSets: @@ -1635,6 +1669,7 @@ export class AVMAPI extends JRPCAPI { * @param payload Optional. Data for NFT Payload as either a [[PayloadBase]] or a {@link https://github.com/feross/buffer|Buffer} * @param memo Optional CB58 Buffer or String which contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction ([[UnsignedTx]]) which contains an [[OperationTx]]. * diff --git a/src/apis/avm/utxos.ts b/src/apis/avm/utxos.ts index 57463e7f0..55f79c7e0 100644 --- a/src/apis/avm/utxos.ts +++ b/src/apis/avm/utxos.ts @@ -343,7 +343,8 @@ export class UTXOSet extends StandardUTXOSet { * @param memo Optional. Contains arbitrary data, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param locktime Optional. The locktime field created in the resulting outputs - * @param threshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param toThreshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param changethreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction created from the passed in parameters. * @@ -361,9 +362,10 @@ export class UTXOSet extends StandardUTXOSet { memo: Buffer = undefined, asOf: BN = UnixNow(), locktime: BN = new BN(0), - threshold: number = 1 + toThreshold: number = 1, + changeThreshold: number = 1 ): UnsignedTx => { - if (threshold > toAddresses.length) { + if (toThreshold > toAddresses.length) { /* istanbul ignore next */ throw new ThresholdError( "Error - UTXOSet.buildBaseTx: threshold is greater than number of addresses" @@ -386,8 +388,10 @@ export class UTXOSet extends StandardUTXOSet { const aad: AssetAmountDestination = new AssetAmountDestination( toAddresses, + toThreshold, fromAddresses, - changeAddresses + changeAddresses, + changeThreshold ) if (assetID.toString("hex") === feeAssetID.toString("hex")) { aad.addAssetAmount(assetID, amount, fee) @@ -401,12 +405,7 @@ export class UTXOSet extends StandardUTXOSet { let ins: TransferableInput[] = [] let outs: TransferableOutput[] = [] - const success: Error = this.getMinimumSpendable( - aad, - asOf, - locktime, - threshold - ) + const success: Error = this.getMinimumSpendable(aad, asOf, locktime) if (typeof success === "undefined") { ins = aad.getInputs() outs = aad.getAllOutputs() @@ -435,6 +434,7 @@ export class UTXOSet extends StandardUTXOSet { * @param feeAssetID Optional. The assetID of the fees being burned. * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} + * @param changethreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction created from the passed in parameters. * @@ -452,7 +452,8 @@ export class UTXOSet extends StandardUTXOSet { fee: BN = undefined, feeAssetID: Buffer = undefined, memo: Buffer = undefined, - asOf: BN = UnixNow() + asOf: BN = UnixNow(), + changeThreshold: number = 1 ): UnsignedTx => { const zero: BN = new BN(0) let ins: TransferableInput[] = [] @@ -460,9 +461,11 @@ export class UTXOSet extends StandardUTXOSet { if (this._feeCheck(fee, feeAssetID)) { const aad: AssetAmountDestination = new AssetAmountDestination( + [], + 0, fromAddresses, - fromAddresses, - changeAddresses + changeAddresses, + changeThreshold ) aad.addAssetAmount(feeAssetID, zero, fee) const success: Error = this.getMinimumSpendable(aad, asOf) @@ -514,6 +517,9 @@ export class UTXOSet extends StandardUTXOSet { * @param feeAssetID Optional. The assetID of the fees being burned. * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} + * @param changethreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO + * + * @returns An unsigned transaction created from the passed in parameters. */ buildSECPMintTx = ( networkID: number, @@ -526,7 +532,8 @@ export class UTXOSet extends StandardUTXOSet { fee: BN = undefined, feeAssetID: Buffer = undefined, memo: Buffer = undefined, - asOf: BN = UnixNow() + asOf: BN = UnixNow(), + changeThreshold: number = 1 ): UnsignedTx => { const zero: BN = new BN(0) let ins: TransferableInput[] = [] @@ -534,9 +541,11 @@ export class UTXOSet extends StandardUTXOSet { if (this._feeCheck(fee, feeAssetID)) { const aad: AssetAmountDestination = new AssetAmountDestination( + [], + 0, fromAddresses, - fromAddresses, - changeAddresses + changeAddresses, + changeThreshold ) aad.addAssetAmount(feeAssetID, zero, fee) const success: Error = this.getMinimumSpendable(aad, asOf) @@ -608,6 +617,7 @@ export class UTXOSet extends StandardUTXOSet { * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param locktime Optional. The locktime field created in the resulting mint output + * @param changethreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction created from the passed in parameters. * @@ -624,7 +634,8 @@ export class UTXOSet extends StandardUTXOSet { feeAssetID: Buffer = undefined, memo: Buffer = undefined, asOf: BN = UnixNow(), - locktime: BN = undefined + locktime: BN = undefined, + changeThreshold: number = 1 ): UnsignedTx => { const zero: BN = new BN(0) let ins: TransferableInput[] = [] @@ -632,9 +643,11 @@ export class UTXOSet extends StandardUTXOSet { if (this._feeCheck(fee, feeAssetID)) { const aad: AssetAmountDestination = new AssetAmountDestination( + [], + 0, fromAddresses, - fromAddresses, - changeAddresses + changeAddresses, + changeThreshold ) aad.addAssetAmount(feeAssetID, zero, fee) const success: Error = this.getMinimumSpendable(aad, asOf) @@ -686,6 +699,7 @@ export class UTXOSet extends StandardUTXOSet { * @param feeAssetID Optional. The assetID of the fees being burned. * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} + * @param changethreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction created from the passed in parameters. * @@ -702,7 +716,8 @@ export class UTXOSet extends StandardUTXOSet { fee: BN = undefined, feeAssetID: Buffer = undefined, memo: Buffer = undefined, - asOf: BN = UnixNow() + asOf: BN = UnixNow(), + changeThreshold: number = 1 ): UnsignedTx => { const zero: BN = new BN(0) let ins: TransferableInput[] = [] @@ -710,9 +725,11 @@ export class UTXOSet extends StandardUTXOSet { if (this._feeCheck(fee, feeAssetID)) { const aad: AssetAmountDestination = new AssetAmountDestination( + [], + 0, fromAddresses, - fromAddresses, - changeAddresses + changeAddresses, + changeThreshold ) aad.addAssetAmount(feeAssetID, zero, fee) const success: Error = this.getMinimumSpendable(aad, asOf) @@ -779,7 +796,8 @@ export class UTXOSet extends StandardUTXOSet { * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param locktime Optional. The locktime field created in the resulting outputs - * @param threshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param toThreshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param changethreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction created from the passed in parameters. * @@ -796,7 +814,8 @@ export class UTXOSet extends StandardUTXOSet { memo: Buffer = undefined, asOf: BN = UnixNow(), locktime: BN = new BN(0), - threshold: number = 1 + toThreshold: number = 1, + changeThreshold: number = 1 ): UnsignedTx => { const zero: BN = new BN(0) let ins: TransferableInput[] = [] @@ -804,9 +823,11 @@ export class UTXOSet extends StandardUTXOSet { if (this._feeCheck(fee, feeAssetID)) { const aad: AssetAmountDestination = new AssetAmountDestination( + [], + 0, fromAddresses, - fromAddresses, - changeAddresses + changeAddresses, + changeThreshold ) aad.addAssetAmount(feeAssetID, zero, fee) const success: Error = this.getMinimumSpendable(aad, asOf) @@ -829,7 +850,7 @@ export class UTXOSet extends StandardUTXOSet { out.getPayload(), toAddresses, locktime, - threshold + toThreshold ) const op: NFTTransferOperation = new NFTTransferOperation(outbound) @@ -878,7 +899,9 @@ export class UTXOSet extends StandardUTXOSet { * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param locktime Optional. The locktime field created in the resulting outputs - * @param threshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param toThreshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param changethreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO + * * @returns An unsigned transaction created from the passed in parameters. * */ @@ -895,7 +918,8 @@ export class UTXOSet extends StandardUTXOSet { memo: Buffer = undefined, asOf: BN = UnixNow(), locktime: BN = new BN(0), - threshold: number = 1 + toThreshold: number = 1, + changeThreshold: number = 1 ): UnsignedTx => { const zero: BN = new BN(0) let ins: TransferableInput[] = [] @@ -961,7 +985,7 @@ export class UTXOSet extends StandardUTXOSet { infeeamount, toAddresses, locktime, - threshold + toThreshold ) as AmountOutput const xferout: TransferableOutput = new TransferableOutput( assetID, @@ -976,16 +1000,13 @@ export class UTXOSet extends StandardUTXOSet { if (feeRemaining.gt(zero) && this._feeCheck(feeRemaining, feeAssetID)) { const aad: AssetAmountDestination = new AssetAmountDestination( toAddresses, + toThreshold, fromAddresses, - changeAddresses + changeAddresses, + changeThreshold ) aad.addAssetAmount(feeAssetID, zero, feeRemaining) - const success: Error = this.getMinimumSpendable( - aad, - asOf, - locktime, - threshold - ) + const success: Error = this.getMinimumSpendable(aad, asOf, locktime) if (typeof success === "undefined") { ins = aad.getInputs() outs = aad.getAllOutputs() @@ -1022,7 +1043,9 @@ export class UTXOSet extends StandardUTXOSet { * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param locktime Optional. The locktime field created in the resulting outputs - * @param threshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param toThreshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param changethreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO + * * @returns An unsigned transaction created from the passed in parameters. * */ @@ -1040,7 +1063,8 @@ export class UTXOSet extends StandardUTXOSet { memo: Buffer = undefined, asOf: BN = UnixNow(), locktime: BN = new BN(0), - threshold: number = 1 + toThreshold: number = 1, + changeThreshold: number = 1 ): UnsignedTx => { let ins: TransferableInput[] = [] let outs: TransferableOutput[] = [] @@ -1066,8 +1090,10 @@ export class UTXOSet extends StandardUTXOSet { const aad: AssetAmountDestination = new AssetAmountDestination( toAddresses, + toThreshold, fromAddresses, - changeAddresses + changeAddresses, + changeThreshold ) if (assetID.toString("hex") === feeAssetID.toString("hex")) { aad.addAssetAmount(assetID, amount, fee) @@ -1077,12 +1103,7 @@ export class UTXOSet extends StandardUTXOSet { aad.addAssetAmount(feeAssetID, zero, fee) } } - const success: Error = this.getMinimumSpendable( - aad, - asOf, - locktime, - threshold - ) + const success: Error = this.getMinimumSpendable(aad, asOf, locktime) if (typeof success === "undefined") { ins = aad.getInputs() outs = aad.getChangeOutputs() diff --git a/src/apis/evm/utxos.ts b/src/apis/evm/utxos.ts index 1662b7a52..6c90c72fc 100644 --- a/src/apis/evm/utxos.ts +++ b/src/apis/evm/utxos.ts @@ -436,7 +436,8 @@ export class UTXOSet extends StandardUTXOSet { * @param feeAssetID Optional. The assetID of the fees being burned. * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param locktime Optional. The locktime field created in the resulting outputs - * @param threshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param toThreshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param changethreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * @returns An unsigned transaction created from the passed in parameters. * */ @@ -453,7 +454,8 @@ export class UTXOSet extends StandardUTXOSet { feeAssetID: Buffer = undefined, asOf: BN = UnixNow(), locktime: BN = new BN(0), - threshold: number = 1 + toThreshold: number = 1, + changeThreshold: number = 1 ): UnsignedTx => { let ins: EVMInput[] = [] let exportouts: TransferableOutput[] = [] @@ -483,8 +485,10 @@ export class UTXOSet extends StandardUTXOSet { const aad: AssetAmountDestination = new AssetAmountDestination( toAddresses, + toThreshold, fromAddresses, - changeAddresses + changeAddresses, + changeThreshold ) if (avaxAssetID.toString("hex") === feeAssetID.toString("hex")) { aad.addAssetAmount(avaxAssetID, amount, fee) @@ -494,12 +498,7 @@ export class UTXOSet extends StandardUTXOSet { aad.addAssetAmount(feeAssetID, zero, fee) } } - const success: Error = this.getMinimumSpendable( - aad, - asOf, - locktime, - threshold - ) + const success: Error = this.getMinimumSpendable(aad, asOf, locktime) if (typeof success === "undefined") { exportouts = aad.getOutputs() } else { diff --git a/src/apis/platformvm/api.ts b/src/apis/platformvm/api.ts index 4daadde2a..174276868 100644 --- a/src/apis/platformvm/api.ts +++ b/src/apis/platformvm/api.ts @@ -7,7 +7,7 @@ import BN from "bn.js" import AvalancheCore from "../../camino" import { JRPCAPI } from "../../common/jrpcapi" import { RequestResponseData } from "../../common/apibase" -import { ErrorResponseObject } from "../../utils/errors" +import { ErrorResponseObject, UTXOError } from "../../utils/errors" import BinTools from "../../utils/bintools" import { KeyChain } from "./keychain" import { ONEAVAX } from "../../utils/constants" @@ -57,13 +57,15 @@ import { GetMinStakeResponse, GetMaxStakeAmountParams, SpendParams, - SpendReply + SpendReply, + OutputOwner } from "./interfaces" import { TransferableInput } from "./inputs" import { TransferableOutput } from "./outputs" import { Serialization, SerializedType } from "../../utils" import { GenesisData } from "../avm" import { CaminoExecutor } from "./camino_executor" +import { Network } from "../../utils/networks" /** * @ignore @@ -110,6 +112,15 @@ export class PlatformVMAPI extends JRPCAPI { return this.core.getNetwork().P.alias } + /** + * Gets the current network, fetched via avalanche.fetchNetworkSettings. + * + * @returns The current Network + */ + getNetwork = (): Network => { + return this.core.getNetwork() + } + /** * Gets the blockchainID and returns it. * @@ -1259,7 +1270,8 @@ export class PlatformVMAPI extends JRPCAPI { * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param locktime Optional. The locktime field created in the resulting outputs - * @param threshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param toThreshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction ([[UnsignedTx]]) which contains a [[ImportTx]]. * @@ -1276,7 +1288,8 @@ export class PlatformVMAPI extends JRPCAPI { memo: PayloadBase | Buffer = undefined, asOf: BN = UnixNow(), locktime: BN = new BN(0), - threshold: number = 1 + toThreshold: number = 1, + changeThreshold: number = 1 ): Promise => { const to: Buffer[] = this._cleanAddressArray( toAddresses, @@ -1317,21 +1330,43 @@ export class PlatformVMAPI extends JRPCAPI { const atomics: UTXO[] = atomicUTXOs.getAllUTXOs() - const builtUnsignedTx: UnsignedTx = utxoset.buildImportTx( - this.core.getNetworkID(), - bintools.cb58Decode(this.blockchainID), - to, - from, - change, - atomics, - sourceChain, - this.getTxFee(), - avaxAssetID, - memo, - asOf, - locktime, - threshold - ) + var builtUnsignedTx: UnsignedTx + if (this.core.getNetwork().P.lockModeBondDeposit) { + const executor = new CaminoExecutor(this) + + builtUnsignedTx = await executor.buildImportTx( + this.core.getNetworkID(), + bintools.cb58Decode(this.blockchainID), + to, + from, + change, + atomics, + sourceChain, + this.getTxFee(), + avaxAssetID, + memo, + locktime, + toThreshold, + changeThreshold + ) + } else { + builtUnsignedTx = utxoset.buildImportTx( + this.core.getNetworkID(), + bintools.cb58Decode(this.blockchainID), + to, + from, + change, + atomics, + sourceChain, + this.getTxFee(), + avaxAssetID, + memo, + asOf, + locktime, + toThreshold, + changeThreshold + ) + } if (!(await this.checkGooseEgg(builtUnsignedTx))) { /* istanbul ignore next */ @@ -1354,7 +1389,8 @@ export class PlatformVMAPI extends JRPCAPI { * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param locktime Optional. The locktime field created in the resulting outputs - * @param threshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param toThreshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction ([[UnsignedTx]]) which contains an [[ExportTx]]. */ @@ -1368,7 +1404,8 @@ export class PlatformVMAPI extends JRPCAPI { memo: PayloadBase | Buffer = undefined, asOf: BN = UnixNow(), locktime: BN = new BN(0), - threshold: number = 1 + toThreshold: number = 1, + changeThreshold: number = 1 ): Promise => { let prefixes: object = {} toAddresses.map((a: string): void => { @@ -1417,22 +1454,45 @@ export class PlatformVMAPI extends JRPCAPI { const avaxAssetID: Buffer = await this.getAVAXAssetID() - const builtUnsignedTx: UnsignedTx = utxoset.buildExportTx( - this.core.getNetworkID(), - bintools.cb58Decode(this.blockchainID), - amount, - avaxAssetID, - to, - from, - change, - destinationChain, - this.getTxFee(), - avaxAssetID, - memo, - asOf, - locktime, - threshold - ) + var builtUnsignedTx: UnsignedTx + if (this.core.getNetwork().P.lockModeBondDeposit) { + const executor = new CaminoExecutor(this) + + builtUnsignedTx = await executor.buildExportTx( + this.core.getNetworkID(), + bintools.cb58Decode(this.blockchainID), + amount, + avaxAssetID, + to, + from, + change, + destinationChain, + this.getTxFee(), + avaxAssetID, + memo, + locktime, + toThreshold, + changeThreshold + ) + } else { + builtUnsignedTx = utxoset.buildExportTx( + this.core.getNetworkID(), + bintools.cb58Decode(this.blockchainID), + amount, + avaxAssetID, + to, + from, + change, + destinationChain, + this.getTxFee(), + avaxAssetID, + memo, + asOf, + locktime, + toThreshold, + changeThreshold + ) + } if (!(await this.checkGooseEgg(builtUnsignedTx))) { /* istanbul ignore next */ @@ -1456,6 +1516,7 @@ export class PlatformVMAPI extends JRPCAPI { * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param subnetAuthCredentials Optional. An array of index and address to sign for each SubnetAuth. + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction created from the passed in parameters. */ @@ -1471,7 +1532,8 @@ export class PlatformVMAPI extends JRPCAPI { subnetID: string, memo: PayloadBase | Buffer = undefined, asOf: BN = UnixNow(), - subnetAuthCredentials: [number, Buffer][] = [] + subnetAuthCredentials: [number, Buffer][] = [], + changeThreshold: number = 1 ): Promise => { const from: Buffer[] = this._cleanAddressArray( fromAddresses, @@ -1495,22 +1557,45 @@ export class PlatformVMAPI extends JRPCAPI { ) } - const builtUnsignedTx: UnsignedTx = utxoset.buildAddSubnetValidatorTx( - this.core.getNetworkID(), - bintools.cb58Decode(this.blockchainID), - from, - change, - NodeIDStringToBuffer(nodeID), - startTime, - endTime, - weight, - subnetID, - this.getDefaultTxFee(), - avaxAssetID, - memo, - asOf, - subnetAuthCredentials - ) + var builtUnsignedTx: UnsignedTx + if (this.core.getNetwork().P.lockModeBondDeposit) { + const executor = new CaminoExecutor(this) + + builtUnsignedTx = await executor.buildAddSubnetValidatorTx( + this.core.getNetworkID(), + bintools.cb58Decode(this.blockchainID), + from, + change, + NodeIDStringToBuffer(nodeID), + startTime, + endTime, + weight, + subnetID, + this.getDefaultTxFee(), + avaxAssetID, + memo, + subnetAuthCredentials, + changeThreshold + ) + } else { + builtUnsignedTx = utxoset.buildAddSubnetValidatorTx( + this.core.getNetworkID(), + bintools.cb58Decode(this.blockchainID), + from, + change, + NodeIDStringToBuffer(nodeID), + startTime, + endTime, + weight, + subnetID, + this.getDefaultTxFee(), + avaxAssetID, + memo, + asOf, + subnetAuthCredentials, + changeThreshold + ) + } if (!(await this.checkGooseEgg(builtUnsignedTx))) { /* istanbul ignore next */ @@ -1537,6 +1622,8 @@ export class PlatformVMAPI extends JRPCAPI { * @param rewardThreshold Opional. The number of signatures required to spend the funds in the resultant reward UTXO. Default 1. * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} + * @param toThreshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction created from the passed in parameters. */ @@ -1553,7 +1640,9 @@ export class PlatformVMAPI extends JRPCAPI { rewardLocktime: BN = new BN(0), rewardThreshold: number = 1, memo: PayloadBase | Buffer = undefined, - asOf: BN = UnixNow() + asOf: BN = UnixNow(), + toThreshold: number = 1, + changeThreshold: number = 1 ): Promise => { const to: Buffer[] = this._cleanAddressArray( toAddresses, @@ -1593,6 +1682,12 @@ export class PlatformVMAPI extends JRPCAPI { ) } + if (this.core.getNetwork().P.lockModeBondDeposit) { + throw new UTXOError( + "PlatformVMAPI.buildAddDelegatorTx -- not supported in lockmodeBondDeposit" + ) + } + const builtUnsignedTx: UnsignedTx = utxoset.buildAddDelegatorTx( this.core.getNetworkID(), bintools.cb58Decode(this.blockchainID), @@ -1610,7 +1705,9 @@ export class PlatformVMAPI extends JRPCAPI { new BN(0), avaxAssetID, memo, - asOf + asOf, + toThreshold, + changeThreshold ) if (!(await this.checkGooseEgg(builtUnsignedTx))) { @@ -1639,6 +1736,8 @@ export class PlatformVMAPI extends JRPCAPI { * @param rewardThreshold Opional. The number of signatures required to spend the funds in the resultant reward UTXO. Default 1. * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} + * @param toThreshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction created from the passed in parameters. */ @@ -1656,7 +1755,9 @@ export class PlatformVMAPI extends JRPCAPI { rewardLocktime: BN = new BN(0), rewardThreshold: number = 1, memo: PayloadBase | Buffer = undefined, - asOf: BN = UnixNow() + asOf: BN = UnixNow(), + toThreshold: number = 1, + changeThreshold: number = 1 ): Promise => { const to: Buffer[] = this._cleanAddressArray( toAddresses, @@ -1705,8 +1806,8 @@ export class PlatformVMAPI extends JRPCAPI { "PlatformVMAPI.buildAddValidatorTx -- startTime must be in the future and endTime must come after startTime" ) } - var builtUnsignedTx: UnsignedTx + var builtUnsignedTx: UnsignedTx if (this.core.getNetwork().P.lockModeBondDeposit) { const executor = new CaminoExecutor(this) @@ -1724,7 +1825,9 @@ export class PlatformVMAPI extends JRPCAPI { rewardLocktime, rewardThreshold, memo, - avaxAssetID + avaxAssetID, + toThreshold, + changeThreshold ) } else { builtUnsignedTx = utxoset.buildAddValidatorTx( @@ -1745,7 +1848,9 @@ export class PlatformVMAPI extends JRPCAPI { new BN(0), avaxAssetID, memo, - asOf + asOf, + toThreshold, + changeThreshold ) } @@ -1767,6 +1872,7 @@ export class PlatformVMAPI extends JRPCAPI { * @param subnetOwnerThreshold A number indicating the amount of signatures required to add validators to a subnet * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction created from the passed in parameters. */ @@ -1777,7 +1883,8 @@ export class PlatformVMAPI extends JRPCAPI { subnetOwnerAddresses: string[], subnetOwnerThreshold: number, memo: PayloadBase | Buffer = undefined, - asOf: BN = UnixNow() + asOf: BN = UnixNow(), + changeThreshold: number = 1 ): Promise => { const from: Buffer[] = this._cleanAddressArray( fromAddresses, @@ -1800,18 +1907,38 @@ export class PlatformVMAPI extends JRPCAPI { const networkID: number = this.core.getNetworkID() const blockchainID: Buffer = bintools.cb58Decode(this.blockchainID) const fee: BN = this.getCreateSubnetTxFee() - const builtUnsignedTx: UnsignedTx = utxoset.buildCreateSubnetTx( - networkID, - blockchainID, - from, - change, - owners, - subnetOwnerThreshold, - fee, - avaxAssetID, - memo, - asOf - ) + + var builtUnsignedTx: UnsignedTx + if (this.core.getNetwork().P.lockModeBondDeposit) { + const executor = new CaminoExecutor(this) + + builtUnsignedTx = await executor.buildCreateSubnetTx( + networkID, + blockchainID, + from, + change, + owners, + subnetOwnerThreshold, + fee, + avaxAssetID, + memo, + changeThreshold + ) + } else { + builtUnsignedTx = utxoset.buildCreateSubnetTx( + networkID, + blockchainID, + from, + change, + owners, + subnetOwnerThreshold, + fee, + avaxAssetID, + memo, + asOf, + changeThreshold + ) + } if (!(await this.checkGooseEgg(builtUnsignedTx, this.getCreationTxFee()))) { /* istanbul ignore next */ @@ -1835,6 +1962,7 @@ export class PlatformVMAPI extends JRPCAPI { * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param subnetAuthCredentials Optional. An array of index and address to sign for each SubnetAuth. + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction created from the passed in parameters. */ @@ -1849,7 +1977,8 @@ export class PlatformVMAPI extends JRPCAPI { genesisData: string | GenesisData = undefined, memo: PayloadBase | Buffer = undefined, asOf: BN = UnixNow(), - subnetAuthCredentials: [number, Buffer][] = [] + subnetAuthCredentials: [number, Buffer][] = [], + changeThreshold: number = 1 ): Promise => { const from: Buffer[] = this._cleanAddressArray( fromAddresses, @@ -1870,22 +1999,46 @@ export class PlatformVMAPI extends JRPCAPI { const networkID: number = this.core.getNetworkID() const blockchainID: Buffer = bintools.cb58Decode(this.blockchainID) const fee: BN = this.getCreateChainTxFee() - const builtUnsignedTx: UnsignedTx = utxoset.buildCreateChainTx( - networkID, - blockchainID, - from, - change, - subnetID, - chainName, - vmID, - fxIDs, - genesisData, - fee, - avaxAssetID, - memo, - asOf, - subnetAuthCredentials - ) + + var builtUnsignedTx: UnsignedTx + if (this.core.getNetwork().P.lockModeBondDeposit) { + const executor = new CaminoExecutor(this) + + builtUnsignedTx = await executor.buildCreateChainTx( + networkID, + blockchainID, + from, + change, + subnetID, + chainName, + vmID, + fxIDs, + genesisData, + fee, + avaxAssetID, + memo, + subnetAuthCredentials, + changeThreshold + ) + } else { + builtUnsignedTx = utxoset.buildCreateChainTx( + networkID, + blockchainID, + from, + change, + subnetID, + chainName, + vmID, + fxIDs, + genesisData, + fee, + avaxAssetID, + memo, + asOf, + subnetAuthCredentials, + changeThreshold + ) + } if (!(await this.checkGooseEgg(builtUnsignedTx, this.getCreationTxFee()))) { /* istanbul ignore next */ @@ -1905,17 +2058,19 @@ export class PlatformVMAPI extends JRPCAPI { * @param address The consortiumMemberAddress, single or multi-sig. * @param consortiumMemberAuthCredentials An array of index and address to sign for each SubnetAuth. * @param memo Optional contains arbitrary bytes, up to 256 bytes + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction created from the passed in parameters. */ buildRegisterNodeTx = async ( fromAddresses: string[], - changeAddresses: string[], + changeAddresses: string[] = undefined, oldNodeID: string | Buffer = undefined, newNodeID: string | Buffer = undefined, address: Buffer = undefined, consortiumMemberAuthCredentials: [number, Buffer][] = [], - memo: PayloadBase | Buffer = undefined + memo: PayloadBase | Buffer = undefined, + changeThreshold: number = 1 ): Promise => { const from: Buffer[] = this._cleanAddressArray( fromAddresses, @@ -1948,7 +2103,8 @@ export class PlatformVMAPI extends JRPCAPI { consortiumMemberAuthCredentials, fee, avaxAssetID, - memo + memo, + changeThreshold ) if (!(await this.checkGooseEgg(builtUnsignedTx, this.getCreationTxFee()))) { @@ -2077,7 +2233,10 @@ export class PlatformVMAPI extends JRPCAPI { */ spend = async ( from: string[] | string, - changeAddr: string, + to: string[], + toThreshold: number, + change: string[], + changeThreshold: number, lockMode: "Unlocked" | "Deposit" | "Bond", amountToLock: BN, amountToBurn: BN, @@ -2085,7 +2244,14 @@ export class PlatformVMAPI extends JRPCAPI { ): Promise => { const params: SpendParams = { from, - changeAddr, + to: + to.length > 0 + ? { lockTime: 0, threshold: toThreshold, addresses: to } + : undefined, + change: + change.length > 0 + ? { lockTime: 0, threshold: changeThreshold, addresses: change } + : undefined, lockMode: lockMode === "Unlocked" ? 0 : lockMode === "Deposit" ? 1 : 2, amountToLock: amountToLock.toString(), amountToBurn: amountToBurn.toString(), diff --git a/src/apis/platformvm/camino_executor.ts b/src/apis/platformvm/camino_executor.ts index 7615ccda9..a94e7568a 100644 --- a/src/apis/platformvm/camino_executor.ts +++ b/src/apis/platformvm/camino_executor.ts @@ -7,18 +7,38 @@ import BN from "bn.js" import { Buffer } from "buffer/" import { DefaultNetworkID, UnixNow } from "../../utils" -import { TimeError } from "../../utils/errors" +import BinTools from "../../utils/bintools" import { + AddressError, + FeeAssetError, + ThresholdError, + TimeError +} from "../../utils/errors" +import { + AddSubnetValidatorTx, + AmountOutput, AssetAmountDestination, + BaseTx, CaminoAddValidatorTx, + CreateChainTx, + CreateSubnetTx, + ExportTx, + ImportTx, ParseableOutput, PlatformVMAPI, RegisterNodeTx, SECPOwnerOutput, + SECPTransferInput, + SECPTransferOutput, + SelectOutputClass, TransferableInput, TransferableOutput, - UnsignedTx + UnsignedTx, + UTXO } from "." +import { GenesisData } from "../avm" + +const bintools: BinTools = BinTools.getInstance() type CaminoLockMode = "Unlocked" | "Bond" | "Deposit" @@ -37,16 +57,23 @@ export class CaminoExecutor { const addr = aad .getSenders() .map((a) => this.platformAPI.addressFromBuffer(a)) - const changeAddr = - aad.getChangeAddresses().length > 0 - ? this.platformAPI.addressFromBuffer(aad.getChangeAddresses()[0]) - : "" + + const to = aad + .getDestinations() + .map((a) => this.platformAPI.addressFromBuffer(a)) + + const change = aad + .getChangeAddresses() + .map((a) => this.platformAPI.addressFromBuffer(a)) const aa = aad.getAssetAmount(feeAssetID.toString("hex")) const result = await this.platformAPI.spend( addr, - changeAddr, + to, + aad.getDestinationsThreshold(), + change, + aad.getChangeAddressesThreshold(), lockMode, aa.getAmount(), aa.getBurn() @@ -79,6 +106,8 @@ export class CaminoExecutor { * @param rewardLocktime Optional. The locktime field created in the resulting reward outputs * @param rewardThreshold Opional. The number of signatures required to spend the funds in the resultant reward UTXO. Default 1. * @param memo Optional contains arbitrary bytes, up to 256 bytes + * @param toThreshold Optional. The number of signatures required to spend the funds in the received UTXO + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction created from the passed in parameters. */ @@ -96,7 +125,9 @@ export class CaminoExecutor { rewardLocktime: BN = new BN(0), rewardThreshold: number = 1, memo: Buffer = undefined, - avaxAssetID: Buffer + avaxAssetID: Buffer, + toThreshold: number = 1, + changeThreshold: number = 1 ): Promise => { let ins: TransferableInput[] = [] let outs: TransferableOutput[] = [] @@ -111,8 +142,10 @@ export class CaminoExecutor { const aad: AssetAmountDestination = new AssetAmountDestination( to, + toThreshold, from, - change + change, + changeThreshold ) aad.addAssetAmount(avaxAssetID, stakeAmount, new BN(0)) @@ -161,6 +194,7 @@ export class CaminoExecutor { * @param fee Optional. The amount of fees to burn in its smallest denomination, represented as {@link https://github.com/indutny/bn.js/|BN} * @param feeAssetID Optional. The assetID of the fees being burned * @param memo Optional contains arbitrary bytes, up to 256 bytes + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned RegisterNodeTx created from the passed in parameters. */ @@ -175,7 +209,8 @@ export class CaminoExecutor { consortiumMemberAuthCredentials: [number, Buffer][] = [], fee: BN = undefined, feeAssetID: Buffer = undefined, - memo: Buffer = undefined + memo: Buffer = undefined, + changeThreshold: number = 1 ): Promise => { const zero: BN = new BN(0) let ins: TransferableInput[] = [] @@ -183,9 +218,11 @@ export class CaminoExecutor { if (this._feeCheck(fee, feeAssetID)) { const aad: AssetAmountDestination = new AssetAmountDestination( + [], + 0, fromAddresses, - fromAddresses, - changeAddresses + changeAddresses, + changeThreshold ) aad.addAssetAmount(feeAssetID, zero, fee) @@ -225,6 +262,595 @@ export class CaminoExecutor { return new UnsignedTx(registerNodeTx) } + /** + * Creates an [[UnsignedTx]] wrapping a [[BaseTx]]. For more granular control, you may create your own + * [[UnsignedTx]] wrapping a [[BaseTx]] manually (with their corresponding [[TransferableInput]]s and [[TransferableOutput]]s). + * + * @param networkID The number representing NetworkID of the node + * @param blockchainID The {@link https://github.com/feross/buffer|Buffer} representing the BlockchainID for the transaction + * @param amount The amount of the asset to be spent in its smallest denomination, represented as {@link https://github.com/indutny/bn.js/|BN}. + * @param assetID {@link https://github.com/feross/buffer|Buffer} of the asset ID for the UTXO + * @param toAddresses The addresses to send the funds + * @param fromAddresses The addresses being used to send the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} + * @param changeAddresses Optional. The addresses that can spend the change remaining from the spent UTXOs. Default: toAddresses + * @param fee Optional. The amount of fees to burn in its smallest denomination, represented as {@link https://github.com/indutny/bn.js/|BN} + * @param feeAssetID Optional. The assetID of the fees being burned. Default: assetID + * @param memo Optional. Contains arbitrary data, up to 256 bytes + * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} + * @param locktime Optional. The locktime field created in the resulting outputs + * @param toThreshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO + * + * @returns An unsigned transaction created from the passed in parameters. + * + */ + buildBaseTx = async ( + networkID: number, + blockchainID: Buffer, + amount: BN, + assetID: Buffer, + toAddresses: Buffer[], + fromAddresses: Buffer[], + changeAddresses: Buffer[] = undefined, + fee: BN = undefined, + feeAssetID: Buffer = undefined, + memo: Buffer = undefined, + toThreshold: number = 1, + changeThreshold: number = 1 + ): Promise => { + if (toThreshold > toAddresses.length) { + /* istanbul ignore next */ + throw new ThresholdError( + "Error - UTXOSet.buildBaseTx: threshold is greater than number of addresses" + ) + } + + if (typeof changeAddresses === "undefined") { + changeAddresses = [] + } + + if (typeof feeAssetID === "undefined") { + feeAssetID = assetID + } + + const zero: BN = new BN(0) + + if (amount.eq(zero)) { + return undefined + } + + const aad: AssetAmountDestination = new AssetAmountDestination( + toAddresses, + toThreshold, + fromAddresses, + changeAddresses, + changeThreshold + ) + if (assetID.toString("hex") === feeAssetID.toString("hex")) { + aad.addAssetAmount(assetID, amount, fee) + } else { + aad.addAssetAmount(assetID, amount, zero) + if (this._feeCheck(fee, feeAssetID)) { + aad.addAssetAmount(feeAssetID, zero, fee) + } + } + + let ins: TransferableInput[] = [] + let outs: TransferableOutput[] = [] + + const minSpendableErr: Error = await this.spend(aad, "Unlocked", feeAssetID) + if (typeof minSpendableErr === "undefined") { + ins = aad.getInputs() + outs = aad.getAllOutputs() + } else { + throw minSpendableErr + } + + const baseTx: BaseTx = new BaseTx(networkID, blockchainID, outs, ins, memo) + return new UnsignedTx(baseTx) + } + + /** + * Creates an unsigned ImportTx transaction. + * + * @param networkID The number representing NetworkID of the node + * @param blockchainID The {@link https://github.com/feross/buffer|Buffer} representing the BlockchainID for the transaction + * @param toAddresses The addresses to send the funds + * @param fromAddresses The addresses being used to send the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} + * @param changeAddresses Optional. The addresses that can spend the change remaining from the spent UTXOs. Default: toAddresses + * @param importIns An array of [[TransferableInput]]s being imported + * @param sourceChain A {@link https://github.com/feross/buffer|Buffer} for the chainid where the imports are coming from. + * @param fee Optional. The amount of fees to burn in its smallest denomination, represented as {@link https://github.com/indutny/bn.js/|BN}. Fee will come from the inputs first, if they can. + * @param feeAssetID Optional. The assetID of the fees being burned. + * @param memo Optional contains arbitrary bytes, up to 256 bytes + * @param locktime Optional. The locktime field created in the resulting outputs + * @param toThreshold Optional. The number of signatures required to spend the funds in the received UTXO + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO + * + * @returns An unsigned transaction created from the passed in parameters. + * + */ + buildImportTx = async ( + networkID: number, + blockchainID: Buffer, + toAddresses: Buffer[], + fromAddresses: Buffer[], + changeAddresses: Buffer[], + atomics: UTXO[], + sourceChain: Buffer = undefined, + fee: BN = undefined, + feeAssetID: Buffer = undefined, + memo: Buffer = undefined, + locktime: BN = new BN(0), + toThreshold: number = 1, + changeThreshold: number = 1 + ): Promise => { + const zero: BN = new BN(0) + let ins: TransferableInput[] = [] + let outs: TransferableOutput[] = [] + if (typeof fee === "undefined") { + fee = zero.clone() + } + + const importIns: TransferableInput[] = [] + let feepaid: BN = new BN(0) + let feeAssetStr: string = feeAssetID.toString("hex") + for (let i: number = 0; i < atomics.length; i++) { + const utxo: UTXO = atomics[`${i}`] + const assetID: Buffer = utxo.getAssetID() + const output: AmountOutput = utxo.getOutput() as AmountOutput + let amt: BN = output.getAmount().clone() + + let infeeamount = amt.clone() + let assetStr: string = assetID.toString("hex") + if ( + typeof feeAssetID !== "undefined" && + fee.gt(zero) && + feepaid.lt(fee) && + assetStr === feeAssetStr + ) { + feepaid = feepaid.add(infeeamount) + if (feepaid.gte(fee)) { + infeeamount = feepaid.sub(fee) + feepaid = fee.clone() + } else { + infeeamount = zero.clone() + } + } + + const txid: Buffer = utxo.getTxID() + const outputidx: Buffer = utxo.getOutputIdx() + const input: SECPTransferInput = new SECPTransferInput(amt) + const xferin: TransferableInput = new TransferableInput( + txid, + outputidx, + assetID, + input + ) + const from: Buffer[] = output.getAddresses() + const spenders: Buffer[] = output.getSpenders(from) + for (let j: number = 0; j < spenders.length; j++) { + const idx: number = output.getAddressIdx(spenders[`${j}`]) + if (idx === -1) { + /* istanbul ignore next */ + throw new AddressError( + "Error - UTXOSet.buildImportTx: no such " + + `address in output: ${spenders[`${j}`]}` + ) + } + xferin.getInput().addSignatureIdx(idx, spenders[`${j}`]) + } + importIns.push(xferin) + //add extra outputs for each amount (calculated from the imported inputs), minus fees + if (infeeamount.gt(zero)) { + const spendout: AmountOutput = SelectOutputClass( + output.getOutputID(), + infeeamount, + toAddresses, + locktime, + toThreshold + ) as AmountOutput + const xferout: TransferableOutput = new TransferableOutput( + assetID, + spendout + ) + outs.push(xferout) + } + } + + // get remaining fees from the provided addresses + let feeRemaining: BN = fee.sub(feepaid) + if (feeRemaining.gt(zero) && this._feeCheck(feeRemaining, feeAssetID)) { + const aad: AssetAmountDestination = new AssetAmountDestination( + toAddresses, + toThreshold, + fromAddresses, + changeAddresses, + changeThreshold + ) + + aad.addAssetAmount(feeAssetID, zero, feeRemaining) + const minSpendableErr: Error = await this.spend( + aad, + "Unlocked", + feeAssetID + ) + if (typeof minSpendableErr === "undefined") { + ins = aad.getInputs() + outs = aad.getAllOutputs() + } else { + throw minSpendableErr + } + } + + const importTx: ImportTx = new ImportTx( + networkID, + blockchainID, + outs, + ins, + memo, + sourceChain, + importIns + ) + return new UnsignedTx(importTx) + } + + /** + * Creates an unsigned ExportTx transaction. + * + * @param networkID The number representing NetworkID of the node + * @param blockchainID The {@link https://github.com/feross/buffer|Buffer} representing the BlockchainID for the transaction + * @param amount The amount being exported as a {@link https://github.com/indutny/bn.js/|BN} + * @param avaxAssetID {@link https://github.com/feross/buffer|Buffer} of the asset ID for AVAX + * @param toAddresses An array of addresses as {@link https://github.com/feross/buffer|Buffer} who recieves the AVAX + * @param fromAddresses An array of addresses as {@link https://github.com/feross/buffer|Buffer} who owns the AVAX + * @param changeAddresses An array of addresses as {@link https://github.com/feross/buffer|Buffer} who gets the change leftover of the AVAX + * @param destinationChain Optional. A {@link https://github.com/feross/buffer|Buffer} for the chainid where to send the asset. + * @param fee Optional. The amount of fees to burn in its smallest denomination, represented as {@link https://github.com/indutny/bn.js/|BN} + * @param feeAssetID Optional. The assetID of the fees being burned. + * @param memo Optional contains arbitrary bytes, up to 256 bytes + * @param locktime Optional. The locktime field created in the resulting outputs + * @param toThreshold Optional. The number of signatures required to spend the funds in the received UTXO + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO + * + * @returns An unsigned transaction created from the passed in parameters. + * + */ + buildExportTx = async ( + networkID: number, + blockchainID: Buffer, + amount: BN, + avaxAssetID: Buffer, // TODO: rename this to amountAssetID + toAddresses: Buffer[], + fromAddresses: Buffer[], + changeAddresses: Buffer[] = undefined, + destinationChain: Buffer = undefined, + fee: BN = undefined, + feeAssetID: Buffer = undefined, + memo: Buffer = undefined, + locktime: BN = new BN(0), + toThreshold: number = 1, + changeThreshold: number = 1 + ): Promise => { + let ins: TransferableInput[] = [] + let outs: TransferableOutput[] = [] + + if (typeof changeAddresses === "undefined") { + changeAddresses = toAddresses + } + + const zero: BN = new BN(0) + + if (amount.eq(zero)) { + return undefined + } + + if (typeof feeAssetID === "undefined") { + feeAssetID = avaxAssetID + } else if (feeAssetID.toString("hex") !== avaxAssetID.toString("hex")) { + /* istanbul ignore next */ + throw new FeeAssetError( + "Error - CaminoExecutor.buildExportTx: " + + `feeAssetID must match avaxAssetID` + ) + } + + if (typeof destinationChain === "undefined") { + destinationChain = bintools.cb58Decode( + this.platformAPI.getNetwork().X.blockchainID + ) + } + + const aad: AssetAmountDestination = new AssetAmountDestination( + [], + 0, + fromAddresses, + changeAddresses, + changeThreshold + ) + + aad.addAssetAmount(avaxAssetID, new BN(0), fee.add(amount)) + + const minSpendableErr: Error = await this.spend(aad, "Unlocked", feeAssetID) + if (typeof minSpendableErr === "undefined") { + ins = aad.getInputs() + outs = aad.getAllOutputs() + } else { + throw minSpendableErr + } + + const exportTx: ExportTx = new ExportTx( + networkID, + blockchainID, + outs, + ins, + memo, + destinationChain, + [ + new SECPTransferOutput( + amount, + toAddresses, + locktime, + toThreshold + ).makeTransferable(avaxAssetID) + ] + ) + + return new UnsignedTx(exportTx) + } + + /** + * Class representing an unsigned [[AddSubnetValidatorTx]] transaction. + * + * @param networkID Networkid, [[DefaultNetworkID]] + * @param blockchainID Blockchainid, default undefined + * @param fromAddresses An array of addresses as {@link https://github.com/feross/buffer|Buffer} who pays the fees in AVAX + * @param changeAddresses An array of addresses as {@link https://github.com/feross/buffer|Buffer} who gets the change leftover from the fee payment + * @param nodeID The node ID of the validator being added. + * @param startTime The Unix time when the validator starts validating the Primary Network. + * @param endTime The Unix time when the validator stops validating the Primary Network (and staked AVAX is returned). + * @param weight The amount of weight for this subnet validator. + * @param fee Optional. The amount of fees to burn in its smallest denomination, represented as {@link https://github.com/indutny/bn.js/|BN} + * @param feeAssetID Optional. The assetID of the fees being burned. + * @param memo Optional contains arbitrary bytes, up to 256 bytes + * @param subnetAuthCredentials Optional. An array of index and address to sign for each SubnetAuth. + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO + * + * @returns An unsigned transaction created from the passed in parameters. + */ + buildAddSubnetValidatorTx = async ( + networkID: number = DefaultNetworkID, + blockchainID: Buffer, + fromAddresses: Buffer[], + changeAddresses: Buffer[], + nodeID: Buffer, + startTime: BN, + endTime: BN, + weight: BN, + subnetID: string, + fee: BN = undefined, + feeAssetID: Buffer = undefined, + memo: Buffer = undefined, + subnetAuthCredentials: [number, Buffer][] = [], + changeThreshold: number = 1 + ): Promise => { + let ins: TransferableInput[] = [] + let outs: TransferableOutput[] = [] + + const zero: BN = new BN(0) + const now: BN = UnixNow() + if (startTime.lt(now) || endTime.lte(startTime)) { + throw new Error( + "CaminoExecutor.buildAddSubnetValidatorTx -- startTime must be in the future and endTime must come after startTime" + ) + } + + if (this._feeCheck(fee, feeAssetID)) { + const aad: AssetAmountDestination = new AssetAmountDestination( + [], + 0, + fromAddresses, + changeAddresses, + changeThreshold + ) + + aad.addAssetAmount(feeAssetID, zero, fee) + const minSpendableErr: Error = await this.spend( + aad, + "Unlocked", + feeAssetID + ) + if (typeof minSpendableErr === "undefined") { + ins = aad.getInputs() + outs = aad.getAllOutputs() + } else { + throw minSpendableErr + } + } + + const addSubnetValidatorTx: AddSubnetValidatorTx = new AddSubnetValidatorTx( + networkID, + blockchainID, + outs, + ins, + memo, + nodeID, + startTime, + endTime, + weight, + subnetID + ) + subnetAuthCredentials.forEach( + (subnetAuthCredential: [number, Buffer]): void => { + addSubnetValidatorTx.addSignatureIdx( + subnetAuthCredential[0], + subnetAuthCredential[1] + ) + } + ) + return new UnsignedTx(addSubnetValidatorTx) + } + + /** + * Class representing an unsigned [[CreateSubnetTx]] transaction. + * + * @param networkID Networkid, [[DefaultNetworkID]] + * @param blockchainID Blockchainid, default undefined + * @param fromAddresses The addresses being used to send the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} + * @param changeAddresses The addresses that can spend the change remaining from the spent UTXOs. + * @param subnetOwnerAddresses An array of {@link https://github.com/feross/buffer|Buffer} for the addresses to add to a subnet + * @param subnetOwnerThreshold The number of owners's signatures required to add a validator to the network + * @param fee Optional. The amount of fees to burn in its smallest denomination, represented as {@link https://github.com/indutny/bn.js/|BN} + * @param feeAssetID Optional. The assetID of the fees being burned + * @param memo Optional contains arbitrary bytes, up to 256 bytes + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO + * + * @returns An unsigned transaction created from the passed in parameters. + */ + buildCreateSubnetTx = async ( + networkID: number = DefaultNetworkID, + blockchainID: Buffer, + fromAddresses: Buffer[], + changeAddresses: Buffer[], + subnetOwnerAddresses: Buffer[], + subnetOwnerThreshold: number, + fee: BN = undefined, + feeAssetID: Buffer = undefined, + memo: Buffer = undefined, + changeThreshold: number = 1 + ): Promise => { + const zero: BN = new BN(0) + let ins: TransferableInput[] = [] + let outs: TransferableOutput[] = [] + + if (this._feeCheck(fee, feeAssetID)) { + const aad: AssetAmountDestination = new AssetAmountDestination( + [], + 0, + fromAddresses, + changeAddresses, + changeThreshold + ) + + aad.addAssetAmount(feeAssetID, zero, fee) + const minSpendableErr: Error = await this.spend( + aad, + "Unlocked", + feeAssetID + ) + if (typeof minSpendableErr === "undefined") { + ins = aad.getInputs() + outs = aad.getAllOutputs() + } else { + throw minSpendableErr + } + } + + const locktime: BN = new BN(0) + const subnetOwners: SECPOwnerOutput = new SECPOwnerOutput( + subnetOwnerAddresses, + locktime, + subnetOwnerThreshold + ) + const createSubnetTx: CreateSubnetTx = new CreateSubnetTx( + networkID, + blockchainID, + outs, + ins, + memo, + subnetOwners + ) + + return new UnsignedTx(createSubnetTx) + } + + /** + * Build an unsigned [[CreateChainTx]]. + * + * @param networkID Networkid, [[DefaultNetworkID]] + * @param blockchainID Blockchainid, default undefined + * @param fromAddresses The addresses being used to send the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} + * @param changeAddresses The addresses that can spend the change remaining from the spent UTXOs. + * @param subnetID Optional ID of the Subnet that validates this blockchain + * @param chainName Optional A human readable name for the chain; need not be unique + * @param vmID Optional ID of the VM running on the new chain + * @param fxIDs Optional IDs of the feature extensions running on the new chain + * @param genesisData Optional Byte representation of genesis state of the new chain + * @param fee Optional. The amount of fees to burn in its smallest denomination, represented as {@link https://github.com/indutny/bn.js/|BN} + * @param feeAssetID Optional. The assetID of the fees being burned + * @param memo Optional contains arbitrary bytes, up to 256 bytes + * @param subnetAuthCredentials Optional. An array of index and address to sign for each SubnetAuth. + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO + * + * @returns An unsigned CreateChainTx created from the passed in parameters. + */ + buildCreateChainTx = async ( + networkID: number = DefaultNetworkID, + blockchainID: Buffer, + fromAddresses: Buffer[], + changeAddresses: Buffer[], + subnetID: string | Buffer = undefined, + chainName: string = undefined, + vmID: string = undefined, + fxIDs: string[] = undefined, + genesisData: string | GenesisData = undefined, + fee: BN = undefined, + feeAssetID: Buffer = undefined, + memo: Buffer = undefined, + subnetAuthCredentials: [number, Buffer][] = [], + changeThreshold: number = 1 + ): Promise => { + const zero: BN = new BN(0) + let ins: TransferableInput[] = [] + let outs: TransferableOutput[] = [] + + if (this._feeCheck(fee, feeAssetID)) { + const aad: AssetAmountDestination = new AssetAmountDestination( + [], + 0, + fromAddresses, + changeAddresses, + changeThreshold + ) + + aad.addAssetAmount(feeAssetID, zero, fee) + const minSpendableErr: Error = await this.spend( + aad, + "Unlocked", + feeAssetID + ) + if (typeof minSpendableErr === "undefined") { + ins = aad.getInputs() + outs = aad.getAllOutputs() + } else { + throw minSpendableErr + } + } + + const createChainTx: CreateChainTx = new CreateChainTx( + networkID, + blockchainID, + outs, + ins, + memo, + subnetID, + chainName, + vmID, + fxIDs, + genesisData + ) + subnetAuthCredentials.forEach( + (subnetAuthCredential: [number, Buffer]): void => { + createChainTx.addSignatureIdx( + subnetAuthCredential[0], + subnetAuthCredential[1] + ) + } + ) + + return new UnsignedTx(createChainTx) + } + _feeCheck(fee: BN, feeAssetID: Buffer): boolean { return ( typeof fee !== "undefined" && diff --git a/src/apis/platformvm/interfaces.ts b/src/apis/platformvm/interfaces.ts index 8fe42d9d3..938957e65 100644 --- a/src/apis/platformvm/interfaces.ts +++ b/src/apis/platformvm/interfaces.ts @@ -222,9 +222,17 @@ export interface GetMaxStakeAmountParams { endTime: BN } +export interface OutputOwner { + lockTime: number + threshold: number + addresses: string[] +} + export interface SpendParams { from: string[] | string - changeAddr: string + to?: OutputOwner + change?: OutputOwner + lockMode: 0 | 1 | 2 amountToLock: string amountToBurn: string diff --git a/src/apis/platformvm/utxos.ts b/src/apis/platformvm/utxos.ts index 35af6ad03..88d4349c5 100644 --- a/src/apis/platformvm/utxos.ts +++ b/src/apis/platformvm/utxos.ts @@ -256,7 +256,6 @@ export class UTXOSet extends StandardUTXOSet { aad: AssetAmountDestination, asOf: BN = UnixNow(), locktime: BN = new BN(0), - threshold: number = 1, stakeable: boolean = false ): Error => { let utxoArray: UTXO[] = this.getConsumableUXTO(asOf, stakeable) @@ -482,7 +481,7 @@ export class UTXOSet extends StandardUTXOSet { unlockedChange, aad.getChangeAddresses(), zero.clone(), // make sure that we don't lock the change output. - threshold + aad.getChangeAddressesThreshold() ) as AmountOutput const transferOutput: TransferableOutput = new TransferableOutput( assetID, @@ -509,7 +508,7 @@ export class UTXOSet extends StandardUTXOSet { unlockedAmount, aad.getDestinations(), locktime, - threshold + aad.getDestinationsThreshold() ) as AmountOutput const transferOutput: TransferableOutput = new TransferableOutput( assetID, @@ -537,7 +536,8 @@ export class UTXOSet extends StandardUTXOSet { * @param memo Optional. Contains arbitrary data, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param locktime Optional. The locktime field created in the resulting outputs - * @param threshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param toThreshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction created from the passed in parameters. * @@ -555,9 +555,10 @@ export class UTXOSet extends StandardUTXOSet { memo: Buffer = undefined, asOf: BN = UnixNow(), locktime: BN = new BN(0), - threshold: number = 1 + toThreshold: number = 1, + changeThreshold: number = 1 ): UnsignedTx => { - if (threshold > toAddresses.length) { + if (toThreshold > toAddresses.length) { /* istanbul ignore next */ throw new ThresholdError( "Error - UTXOSet.buildBaseTx: threshold is greater than number of addresses" @@ -580,8 +581,10 @@ export class UTXOSet extends StandardUTXOSet { const aad: AssetAmountDestination = new AssetAmountDestination( toAddresses, + toThreshold, fromAddresses, - changeAddresses + changeAddresses, + changeThreshold ) if (assetID.toString("hex") === feeAssetID.toString("hex")) { aad.addAssetAmount(assetID, amount, fee) @@ -595,12 +598,7 @@ export class UTXOSet extends StandardUTXOSet { let ins: TransferableInput[] = [] let outs: TransferableOutput[] = [] - const minSpendableErr: Error = this.getMinimumSpendable( - aad, - asOf, - locktime, - threshold - ) + const minSpendableErr: Error = this.getMinimumSpendable(aad, asOf, locktime) if (typeof minSpendableErr === "undefined") { ins = aad.getInputs() outs = aad.getAllOutputs() @@ -627,7 +625,8 @@ export class UTXOSet extends StandardUTXOSet { * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param locktime Optional. The locktime field created in the resulting outputs - * @param threshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param toThreshold Optional. The number of signatures required to spend the funds in the received UTXO + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * @returns An unsigned transaction created from the passed in parameters. * */ @@ -644,7 +643,8 @@ export class UTXOSet extends StandardUTXOSet { memo: Buffer = undefined, asOf: BN = UnixNow(), locktime: BN = new BN(0), - threshold: number = 1 + toThreshold: number = 1, + changeThreshold: number = 1 ): UnsignedTx => { const zero: BN = new BN(0) let ins: TransferableInput[] = [] @@ -709,7 +709,7 @@ export class UTXOSet extends StandardUTXOSet { infeeamount, toAddresses, locktime, - threshold + toThreshold ) as AmountOutput const xferout: TransferableOutput = new TransferableOutput( assetID, @@ -719,20 +719,25 @@ export class UTXOSet extends StandardUTXOSet { } } + if (typeof changeAddresses === "undefined") { + changeAddresses = fromAddresses + } + // get remaining fees from the provided addresses let feeRemaining: BN = fee.sub(feepaid) if (feeRemaining.gt(zero) && this._feeCheck(feeRemaining, feeAssetID)) { const aad: AssetAmountDestination = new AssetAmountDestination( toAddresses, + toThreshold, fromAddresses, - changeAddresses + changeAddresses, + changeThreshold ) aad.addAssetAmount(feeAssetID, zero, feeRemaining) const minSpendableErr: Error = this.getMinimumSpendable( aad, asOf, - locktime, - threshold + locktime ) if (typeof minSpendableErr === "undefined") { ins = aad.getInputs() @@ -770,7 +775,8 @@ export class UTXOSet extends StandardUTXOSet { * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param locktime Optional. The locktime field created in the resulting outputs - * @param threshold Optional. The number of signatures required to spend the funds in the resultant UTXO + * @param toThreshold Optional. The number of signatures required to spend the funds in the received UTXO + * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction created from the passed in parameters. * @@ -789,14 +795,15 @@ export class UTXOSet extends StandardUTXOSet { memo: Buffer = undefined, asOf: BN = UnixNow(), locktime: BN = new BN(0), - threshold: number = 1 + toThreshold: number = 1, + changeThreshold: number = 1 ): UnsignedTx => { let ins: TransferableInput[] = [] let outs: TransferableOutput[] = [] let exportouts: TransferableOutput[] = [] if (typeof changeAddresses === "undefined") { - changeAddresses = toAddresses + changeAddresses = fromAddresses } const zero: BN = new BN(0) @@ -822,8 +829,10 @@ export class UTXOSet extends StandardUTXOSet { const aad: AssetAmountDestination = new AssetAmountDestination( toAddresses, + toThreshold, fromAddresses, - changeAddresses + changeAddresses, + changeThreshold ) if (avaxAssetID.toString("hex") === feeAssetID.toString("hex")) { aad.addAssetAmount(avaxAssetID, amount, fee) @@ -834,12 +843,7 @@ export class UTXOSet extends StandardUTXOSet { } } - const minSpendableErr: Error = this.getMinimumSpendable( - aad, - asOf, - locktime, - threshold - ) + const minSpendableErr: Error = this.getMinimumSpendable(aad, asOf, locktime) if (typeof minSpendableErr === "undefined") { ins = aad.getInputs() outs = aad.getChangeOutputs() @@ -877,6 +881,7 @@ export class UTXOSet extends StandardUTXOSet { * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param subnetAuthCredentials Optional. An array of index and address to sign for each SubnetAuth. + * @param changeThreshold Optional. The number of signatures required to spend the funds in the change UTXO * * @returns An unsigned transaction created from the passed in parameters. */ @@ -894,7 +899,8 @@ export class UTXOSet extends StandardUTXOSet { feeAssetID: Buffer = undefined, memo: Buffer = undefined, asOf: BN = UnixNow(), - subnetAuthCredentials: [number, Buffer][] = [] + subnetAuthCredentials: [number, Buffer][] = [], + changeThreshold: number = 1 ): UnsignedTx => { let ins: TransferableInput[] = [] let outs: TransferableOutput[] = [] @@ -909,16 +915,17 @@ export class UTXOSet extends StandardUTXOSet { if (this._feeCheck(fee, feeAssetID)) { const aad: AssetAmountDestination = new AssetAmountDestination( + [], + 0, fromAddresses, - fromAddresses, - changeAddresses + changeAddresses, + changeThreshold ) aad.addAssetAmount(feeAssetID, zero, fee) const success: Error = this.getMinimumSpendable( aad, asOf, undefined, - undefined, true ) if (typeof success === "undefined") { @@ -972,6 +979,7 @@ export class UTXOSet extends StandardUTXOSet { * @param feeAssetID Optional. The assetID of the fees being burned. * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} + * @param toThreshold Optional. The number of signatures required to spend the funds in the stake UTXO * @param changeThreshold Optional. The number of signatures required to spend the funds in the change UTXO * * @returns An unsigned transaction created from the passed in parameters. @@ -994,6 +1002,7 @@ export class UTXOSet extends StandardUTXOSet { feeAssetID: Buffer = undefined, memo: Buffer = undefined, asOf: BN = UnixNow(), + toThreshold: number = 1, changeThreshold: number = 1 ): UnsignedTx => { if (rewardThreshold > rewardAddresses.length) { @@ -1021,8 +1030,10 @@ export class UTXOSet extends StandardUTXOSet { const aad: AssetAmountDestination = new AssetAmountDestination( toAddresses, + toThreshold, fromAddresses, - changeAddresses + changeAddresses, + changeThreshold ) if (avaxAssetID.toString("hex") === feeAssetID.toString("hex")) { aad.addAssetAmount(avaxAssetID, stakeAmount, fee) @@ -1037,7 +1048,6 @@ export class UTXOSet extends StandardUTXOSet { aad, asOf, undefined, - changeThreshold, true ) if (typeof minSpendableErr === "undefined") { @@ -1092,8 +1102,8 @@ export class UTXOSet extends StandardUTXOSet { * @param feeAssetID Optional. The assetID of the fees being burned. * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} - * - * @returns An unsigned transaction created from the passed in parameters. + * @param toThreshold Optional. The number of signatures required to spend the funds in the stake UTXO + * @param changeThreshold Optional. The number of signatures required to spend the funds in the change UTXO */ buildAddValidatorTx = ( networkID: number = DefaultNetworkID, @@ -1113,7 +1123,9 @@ export class UTXOSet extends StandardUTXOSet { fee: BN = undefined, feeAssetID: Buffer = undefined, memo: Buffer = undefined, - asOf: BN = UnixNow() + asOf: BN = UnixNow(), + toThreshold: number = 1, + changeThreshold: number = 1 ): UnsignedTx => { let ins: TransferableInput[] = [] let outs: TransferableOutput[] = [] @@ -1135,8 +1147,10 @@ export class UTXOSet extends StandardUTXOSet { const aad: AssetAmountDestination = new AssetAmountDestination( toAddresses, + toThreshold, fromAddresses, - changeAddresses + changeAddresses, + changeThreshold ) if (avaxAssetID.toString("hex") === feeAssetID.toString("hex")) { aad.addAssetAmount(avaxAssetID, stakeAmount, fee) @@ -1151,7 +1165,6 @@ export class UTXOSet extends StandardUTXOSet { aad, asOf, undefined, - undefined, true ) if (typeof minSpendableErr === "undefined") { @@ -1198,6 +1211,7 @@ export class UTXOSet extends StandardUTXOSet { * @param feeAssetID Optional. The assetID of the fees being burned * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} + * @param changeThreshold Optional. The number of signatures required to spend the funds in the change UTXO * * @returns An unsigned transaction created from the passed in parameters. */ @@ -1211,7 +1225,8 @@ export class UTXOSet extends StandardUTXOSet { fee: BN = undefined, feeAssetID: Buffer = undefined, memo: Buffer = undefined, - asOf: BN = UnixNow() + asOf: BN = UnixNow(), + changeThreshold: number = 1 ): UnsignedTx => { const zero: BN = new BN(0) let ins: TransferableInput[] = [] @@ -1219,9 +1234,11 @@ export class UTXOSet extends StandardUTXOSet { if (this._feeCheck(fee, feeAssetID)) { const aad: AssetAmountDestination = new AssetAmountDestination( + [], + 0, fromAddresses, - fromAddresses, - changeAddresses + changeAddresses, + changeThreshold ) aad.addAssetAmount(feeAssetID, zero, fee) const minSpendableErr: Error = this.getMinimumSpendable( @@ -1273,6 +1290,7 @@ export class UTXOSet extends StandardUTXOSet { * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param subnetAuthCredentials Optional. An array of index and address to sign for each SubnetAuth. + * @param changeThreshold Optional. The number of signatures required to spend the funds in the change UTXO * * @returns An unsigned CreateChainTx created from the passed in parameters. */ @@ -1290,7 +1308,8 @@ export class UTXOSet extends StandardUTXOSet { feeAssetID: Buffer = undefined, memo: Buffer = undefined, asOf: BN = UnixNow(), - subnetAuthCredentials: [number, Buffer][] = [] + subnetAuthCredentials: [number, Buffer][] = [], + changeThreshold: number = 1 ): UnsignedTx => { const zero: BN = new BN(0) let ins: TransferableInput[] = [] @@ -1298,9 +1317,11 @@ export class UTXOSet extends StandardUTXOSet { if (this._feeCheck(fee, feeAssetID)) { const aad: AssetAmountDestination = new AssetAmountDestination( + [], + 0, fromAddresses, - fromAddresses, - changeAddresses + changeAddresses, + changeThreshold ) aad.addAssetAmount(feeAssetID, zero, fee) const minSpendableErr: Error = this.getMinimumSpendable( diff --git a/src/common/assetamount.ts b/src/common/assetamount.ts index e205fe0de..a22e849b8 100644 --- a/src/common/assetamount.ts +++ b/src/common/assetamount.ts @@ -113,8 +113,10 @@ export abstract class StandardAssetAmountDestination< > { protected amounts: AssetAmount[] = [] protected destinations: Buffer[] = [] + protected destinationsThreshold: number = 0 protected senders: Buffer[] = [] protected changeAddresses: Buffer[] = [] + protected changeAddressesThreshold: number = 0 protected amountkey: object = {} protected inputs: TI[] = [] protected outputs: TO[] = [] @@ -148,6 +150,10 @@ export abstract class StandardAssetAmountDestination< return this.destinations } + getDestinationsThreshold = (): number => { + return this.destinationsThreshold + } + getSenders = (): Buffer[] => { return this.senders } @@ -156,6 +162,10 @@ export abstract class StandardAssetAmountDestination< return this.changeAddresses } + getChangeAddressesThreshold = (): number => { + return this.changeAddressesThreshold + } + getAssetAmount = (assetHexStr: string): AssetAmount => { return this.amountkey[`${assetHexStr}`] } @@ -191,11 +201,15 @@ export abstract class StandardAssetAmountDestination< constructor( destinations: Buffer[], + destinationsThreshold: number, senders: Buffer[], - changeAddresses: Buffer[] + changeAddresses: Buffer[], + changeAddressesThreshold: number ) { this.destinations = destinations + this, (destinationsThreshold = destinationsThreshold) this.changeAddresses = changeAddresses + this.changeAddressesThreshold = changeAddressesThreshold this.senders = senders } }