From 491387630ecd7879870daddb88c957df0d639d1f Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Thu, 9 Jan 2025 23:20:04 +0100 Subject: [PATCH 1/4] Update makeCep18TransferDeploy to use contractPackageHash instead of contractHash --- src/utils/cep-18-transfer.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/utils/cep-18-transfer.ts b/src/utils/cep-18-transfer.ts index caa3d748..e2711373 100644 --- a/src/utils/cep-18-transfer.ts +++ b/src/utils/cep-18-transfer.ts @@ -11,12 +11,12 @@ import { Key, KeyTypeID, PublicKey, - StoredContractByHash + StoredVersionedContractByHash } from '../types'; import { CasperNetworkName } from '../@types'; export interface IMakeCep18TransferDeployParams { - contractHash: string; + contractPackageHash: string; senderPublicKeyHex: string; recipientPublicKeyHex: string; transferAmount: string; @@ -29,8 +29,8 @@ export interface IMakeCep18TransferDeployParams { * This function generates a `Deploy` for transferring CEP-18 from one account to another. * * @param params - The parameters required to create the CEP-18 transfer deploy. - * @param params.contractHash - The hash of the contract to interact with. - * This is a 64-character hexadecimal string representing the contract. + * @param params.contractPackageHash - The hash of the contract package to interact with. + * This is a 64-character hexadecimal string representing the contract package. * @param params.senderPublicKeyHex - The sender's public key in hexadecimal format. * @param params.recipientPublicKeyHex - The recipient's public key in hexadecimal format. * @param params.transferAmount - The amount of CSPR to transfer. @@ -62,7 +62,7 @@ export interface IMakeCep18TransferDeployParams { */ export const makeCep18TransferDeploy = ({ - contractHash, + contractPackageHash, senderPublicKeyHex, recipientPublicKeyHex, transferAmount, @@ -75,8 +75,8 @@ export const makeCep18TransferDeploy = ({ const session = new ExecutableDeployItem(); - session.storedContractByHash = new StoredContractByHash( - ContractHash.newContract(contractHash), + session.storedVersionedContractByHash = new StoredVersionedContractByHash( + ContractHash.newContract(contractPackageHash), 'transfer', Args.fromMap({ recipient: CLValue.newCLKey( @@ -86,7 +86,7 @@ export const makeCep18TransferDeploy = ({ ) ), amount: CLValueUInt256.newCLUInt256(transferAmount) - }) + }), ); const payment = ExecutableDeployItem.standardPayment(paymentAmount); From 73055af6d507d4a34127faa655942cdf7d2bf7ec Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Fri, 10 Jan 2025 00:29:00 +0100 Subject: [PATCH 2/4] Update PEM file creation and parsing --- src/types/keypair/PrivateKey.ts | 12 ++-- src/types/keypair/ed25519/PrivateKey.ts | 69 +++++++++++++++++------ src/types/keypair/secp256k1/PrivateKey.ts | 33 ++++++----- src/types/keypair/utils.ts | 31 ++++++++++ 4 files changed, 107 insertions(+), 38 deletions(-) create mode 100644 src/types/keypair/utils.ts diff --git a/src/types/keypair/PrivateKey.ts b/src/types/keypair/PrivateKey.ts index 55b81ef5..82facb97 100644 --- a/src/types/keypair/PrivateKey.ts +++ b/src/types/keypair/PrivateKey.ts @@ -119,15 +119,15 @@ export class PrivateKey { * @param algorithm - The cryptographic algorithm to use. * @returns A promise resolving to a PrivateKey instance. */ - public static async fromPem( + public static fromPem( content: string, algorithm: KeyAlgorithm - ): Promise { - const priv = await PrivateKeyFactory.createPrivateKeyFromPem( + ): PrivateKey { + const priv = PrivateKeyFactory.createPrivateKeyFromPem( content, algorithm ); - const pubBytes = await priv.publicKeyBytes(); + const pubBytes = priv.publicKeyBytes(); const algBytes = Uint8Array.of(algorithm); const pub = PublicKey.fromBuffer(concat([algBytes, pubBytes])); return new PrivateKey(algorithm, pub, priv); @@ -185,10 +185,10 @@ class PrivateKeyFactory { * @returns A promise resolving to a PrivateKeyInternal instance. * @throws Error if the algorithm is unsupported. */ - public static async createPrivateKeyFromPem( + public static createPrivateKeyFromPem( content: string, algorithm: KeyAlgorithm - ): Promise { + ): PrivateKeyInternal { switch (algorithm) { case KeyAlgorithm.ED25519: return Ed25519PrivateKey.fromPem(content); diff --git a/src/types/keypair/ed25519/PrivateKey.ts b/src/types/keypair/ed25519/PrivateKey.ts index 81ef8fdd..20b5a7a1 100644 --- a/src/types/keypair/ed25519/PrivateKey.ts +++ b/src/types/keypair/ed25519/PrivateKey.ts @@ -1,9 +1,13 @@ import * as ed25519 from '@noble/ed25519'; -import { PrivateKeyInternal } from "../PrivateKey"; +import { PrivateKeyInternal } from '../PrivateKey'; import { sha512 } from '@noble/hashes/sha512'; +import { Conversions } from '../../Conversions'; +import { readBase64WithPEM } from '../utils'; ed25519.utils.sha512Sync = (...m) => sha512(ed25519.utils.concatBytes(...m)); +const ED25519_PEM_SECRET_KEY_TAG = 'PRIVATE KEY'; + /** * Represents an Ed25519 private key, supporting key generation, signing, and PEM encoding. * Provides methods for creating instances from byte arrays, hexadecimal strings, and PEM format. @@ -89,13 +93,33 @@ export class PrivateKey implements PrivateKeyInternal { * @returns A PEM-encoded string of the private key. */ toPem(): string { - const seed = this.key.slice(0, 32); - - const prefix = Buffer.alloc(PrivateKey.PemFramePrivateKeyPrefixSize); - const fullKey = Buffer.concat([prefix, Buffer.from(seed)]); - - const pemString = fullKey.toString('base64'); - return `-----BEGIN PRIVATE KEY-----\n${pemString}\n-----END PRIVATE KEY-----`; + const derPrefix = Buffer.from([ + 48, + 46, + 2, + 1, + 0, + 48, + 5, + 6, + 3, + 43, + 101, + 112, + 4, + 34, + 4, + 32 + ]); + const encoded = Conversions.encodeBase64( + Buffer.concat([derPrefix, Buffer.from(this.key)]) + ); + + return ( + `-----BEGIN ${ED25519_PEM_SECRET_KEY_TAG}-----\n` + + `${encoded}\n` + + `-----END ${ED25519_PEM_SECRET_KEY_TAG}-----\n` + ); } /** @@ -106,18 +130,27 @@ export class PrivateKey implements PrivateKeyInternal { * @throws Error if the content cannot be properly parsed. */ static fromPem(content: string): PrivateKey { - const base64Content = content - .replace('-----BEGIN PRIVATE KEY-----', '') - .replace('-----END PRIVATE KEY-----', '') - .replace(/\n/g, ''); - const fullKey = Buffer.from(base64Content, 'base64'); + const privateKeyBytes = readBase64WithPEM(content); - const data = fullKey.slice(PrivateKey.PemFramePrivateKeyPrefixSize); + return new PrivateKey( + new Uint8Array(Buffer.from(PrivateKey.parsePrivateKey(privateKeyBytes))) + ); + } - const seed = data.slice(-32); - const privateEdDSA = ed25519.utils.randomPrivateKey(); - privateEdDSA.set(seed); + private static parsePrivateKey(bytes: Uint8Array) { + const len = bytes.length; + + // prettier-ignore + const key = + (len === 32) ? bytes : + (len === 64) ? Buffer.from(bytes).slice(0, 32) : + (len > 32 && len < 64) ? Buffer.from(bytes).slice(len % 32) : + null; + + if (key == null || key.length !== 32) { + throw Error(`Unexpected key length: ${len}`); + } - return new PrivateKey(privateEdDSA); + return key; } } diff --git a/src/types/keypair/secp256k1/PrivateKey.ts b/src/types/keypair/secp256k1/PrivateKey.ts index 277e18dc..c944feb1 100644 --- a/src/types/keypair/secp256k1/PrivateKey.ts +++ b/src/types/keypair/secp256k1/PrivateKey.ts @@ -1,16 +1,15 @@ import * as secp256k1 from '@noble/secp256k1'; import { sha256 } from '@noble/hashes/sha256'; import { hmac } from '@noble/hashes/hmac'; -import { PrivateKeyInternal } from "../PrivateKey"; +import { PrivateKeyInternal } from '../PrivateKey'; +import KeyEncoder from 'key-encoder'; +import { Conversions } from '../../Conversions'; +import { readBase64WithPEM } from '../utils'; secp256k1.utils.hmacSha256Sync = (k, ...m) => hmac(sha256, k, secp256k1.utils.concatBytes(...m)); -/** PEM prefix for a private key. */ -const PemPrivateKeyPrefix = '-----BEGIN PRIVATE KEY-----'; - -/** PEM suffix for a private key. */ -const PemPrivateKeySuffix = '-----END PRIVATE KEY-----'; +const keyEncoder = new KeyEncoder('secp256k1'); /** * Represents a secp256k1 private key, supporting key generation, signing, and PEM encoding. @@ -111,8 +110,11 @@ export class PrivateKey implements PrivateKeyInternal { * @returns A PEM-encoded string of the private key. */ toPem(): string { - const keyBase64 = Buffer.from(this.key).toString('base64'); - return `${PemPrivateKeyPrefix}\n${keyBase64}\n${PemPrivateKeySuffix}`; + return keyEncoder.encodePrivate( + Conversions.encodeBase16(this.key), + 'raw', + 'pem' + ); } /** @@ -122,11 +124,14 @@ export class PrivateKey implements PrivateKeyInternal { * @throws Error if the content cannot be properly parsed. */ static fromPem(content: string): PrivateKey { - const base64Key = content - .replace(PemPrivateKeyPrefix, '') - .replace(PemPrivateKeySuffix, '') - .replace(/\s+/g, ''); - const keyBuffer = Buffer.from(base64Key, 'base64'); - return new PrivateKey(new Uint8Array(keyBuffer)); + const privateKeyBytes = readBase64WithPEM(content); + + const rawKeyHex = keyEncoder.encodePrivate( + Buffer.from(privateKeyBytes), + 'der', + 'raw' + ); + + return new PrivateKey(new Uint8Array(Buffer.from(rawKeyHex, 'hex'))); } } diff --git a/src/types/keypair/utils.ts b/src/types/keypair/utils.ts new file mode 100644 index 00000000..dd76537e --- /dev/null +++ b/src/types/keypair/utils.ts @@ -0,0 +1,31 @@ +import { Conversions } from '../Conversions'; + +/** + * Reads in a base64 private key, ignoring the header: `-----BEGIN PUBLIC KEY-----` + * and footer: `-----END PUBLIC KEY-----` + * @param {string} content A .pem private key string with a header and footer + * @returns A base64 private key as a `Uint8Array` + * @remarks + * If the provided base64 `content` string does not include a header/footer, + * it will pass through this function unaffected + * @example + * Example PEM: + * + * ``` + * -----BEGIN PUBLIC KEY-----\r\n + * MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEj1fgdbpNbt06EY/8C+wbBXq6VvG+vCVD\r\n + * Nl74LvVAmXfpdzCWFKbdrnIlX3EFDxkd9qpk35F/kLcqV3rDn/u3dg==\r\n + * -----END PUBLIC KEY-----\r\n + * ``` + */ +export function readBase64WithPEM(content: string): Uint8Array { + const base64 = content + // there are two kinks of line-endings, CRLF(\r\n) and LF(\n) + // we need handle both + .split(/\r?\n/) + .filter(x => !x.startsWith('---')) + .join('') + // remove the line-endings in the end of content + .trim(); + return Conversions.decodeBase64(base64); +} From 3eefa8856a169f7e52902c2e1c24edb09b39d40a Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Sat, 11 Jan 2025 13:59:32 +0100 Subject: [PATCH 3/4] Fix @jsonArrayMember issues --- src/rpc/response.ts | 4 ++-- src/types/key/EntryPointAddr.ts | 4 ++-- src/types/key/URef.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rpc/response.ts b/src/rpc/response.ts index 0dbb75ea..b8f2144e 100644 --- a/src/rpc/response.ts +++ b/src/rpc/response.ts @@ -94,10 +94,10 @@ export class RpcAddressableEntity { @jsonMember({ name: 'entity', constructor: RpcAddressableEntity }) entity: RpcAddressableEntity; - @jsonMember({ name: 'named_keys', constructor: NamedKey }) + @jsonArrayMember(NamedKey, { name: 'named_keys' }) namedKeys: NamedKey[]; - @jsonMember({ name: 'entry_points', constructor: EntryPointValue }) + @jsonArrayMember(EntryPointValue, { name: 'entry_points' }) entryPoints?: EntryPointValue[]; } diff --git a/src/types/key/EntryPointAddr.ts b/src/types/key/EntryPointAddr.ts index 68f8b9db..b2e59b8a 100644 --- a/src/types/key/EntryPointAddr.ts +++ b/src/types/key/EntryPointAddr.ts @@ -1,4 +1,4 @@ -import { jsonArrayMember, jsonMember, jsonObject } from 'typedjson'; +import { jsonMember, jsonObject } from 'typedjson'; import { concat } from '@ethersproject/bytes'; import { EntityAddr } from './EntityAddr'; @@ -57,7 +57,7 @@ class VmCasperV1 { }) entityAddr: EntityAddr; - @jsonArrayMember(Uint8Array, { name: 'NameBytes' }) + @jsonMember(Uint8Array, { name: 'NameBytes' }) nameBytes: Uint8Array; constructor(entityAddr: EntityAddr, nameBytes: Uint8Array) { diff --git a/src/types/key/URef.ts b/src/types/key/URef.ts index d265ff1e..660f8d7c 100644 --- a/src/types/key/URef.ts +++ b/src/types/key/URef.ts @@ -1,4 +1,4 @@ -import { jsonObject, jsonMember, jsonArrayMember } from 'typedjson'; +import { jsonObject, jsonMember } from 'typedjson'; import { IResultWithBytes } from '../clvalue'; import { Conversions } from '../Conversions'; import { concat } from '@ethersproject/bytes'; @@ -34,7 +34,7 @@ export const ByteHashLen = 32; @jsonObject export class URef { /** The unique data (hash) associated with the URef, represented as a 32-byte array. */ - @jsonArrayMember(Number) + @jsonMember(Uint8Array) data: Uint8Array; /** The access permissions assigned to this URef, defined by the `UrefAccess` enum. */ From 63b8ed6d3cd46cf1d47977a55d750fb423ca113e Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Thu, 16 Jan 2025 00:34:07 +0100 Subject: [PATCH 4/4] Add buildFor1_5 to TransactionBuilder --- src/@types/common.ts | 5 +- src/types/TransactionBuilder.md | 24 ++ src/types/TransactionBuilder.test.ts | 139 ++++++++++++ src/types/TransactionBuilder.ts | 322 ++++++++++++++++++++++++++- src/utils/auction-manager.ts | 2 +- 5 files changed, 489 insertions(+), 3 deletions(-) create mode 100644 src/types/TransactionBuilder.test.ts diff --git a/src/@types/common.ts b/src/@types/common.ts index b772d500..571689dc 100644 --- a/src/@types/common.ts +++ b/src/@types/common.ts @@ -6,7 +6,10 @@ export enum CasperNetworkName { export enum AuctionManagerEntryPoint { delegate = 'delegate', undelegate = 'undelegate', - redelegate = 'redelegate' + redelegate = 'redelegate', + addBid = 'add_bid', + withdrawBid = 'withdraw_bid', + activateBid = 'activate_bid', } export enum NFTTokenStandard { diff --git a/src/types/TransactionBuilder.md b/src/types/TransactionBuilder.md index df699f81..5d76e229 100644 --- a/src/types/TransactionBuilder.md +++ b/src/types/TransactionBuilder.md @@ -159,6 +159,18 @@ const transaction = new NativeTransferBuilder() transaction.sign(sender); +// Create a simple native transfer for 1.5 network +const transactionFromDeploy = new NativeTransferBuilder() + .from(sender.publicKey) + .target(PublicKey.fromHex('abcdef0123456789')) + .amount('25000000000') // Amount in motes + .id(Date.now()) + .chainName('casper') + .payment(100_000_000) + .buildFor1_5(); + +transaction.sign(sender); + // Create a contract call const contractCallTransaction = new ContractCallBuilder() .from(sender.publicKey) @@ -169,5 +181,17 @@ const contractCallTransaction = new ContractCallBuilder() .chainName('casper-net-1') .build(); +contractCallTransaction.sign(sender); + +// Create a contract call for 1.5 network +const contractCallTransactionFromDeploy = new ContractCallBuilder() + .from(sender.publicKey) + .byHash('example_contract') + .entryPoint('unstake') + .runtimeArgs(Args.fromMap({ key: 'value' })) + .payment(3_000000000) // Amount in motes + .chainName('casper-net-1') + .buildFor1_5(); + contractCallTransaction.sign(sender); ``` diff --git a/src/types/TransactionBuilder.test.ts b/src/types/TransactionBuilder.test.ts new file mode 100644 index 00000000..dbf15667 --- /dev/null +++ b/src/types/TransactionBuilder.test.ts @@ -0,0 +1,139 @@ +import { expect } from 'chai'; + +import { + ContractCallBuilder, + NativeDelegateBuilder, + NativeTransferBuilder +} from './TransactionBuilder'; +import { PublicKey } from './keypair'; +import { + makeAuctionManagerDeploy, + makeCep18TransferDeploy, + makeCsprTransferDeploy +} from '../utils'; +import { Args } from './Args'; +import { CLValue, CLValueUInt256 } from './clvalue'; +import { Key, KeyTypeID } from './key'; +import { AuctionManagerEntryPoint, CasperNetworkName } from '../@types'; + +describe('Test TransactionBuilder', () => { + it('should create native CSPR transfer Deploy', () => { + const tx = new NativeTransferBuilder() + .from( + PublicKey.fromHex( + '0202f5a92ab6da536e7b1a351406f3744224bec85d7acbab1497b65de48a1a707b64' + ) + ) + .target( + PublicKey.fromHex( + '0202f5a92ab6da536e7b1a351406f3744224bec85d7acbab1497b65de48a1a707b64' + ) + ) + .amount('25000000000') + .chainName('casper') + .payment(100_000_000) + .buildFor1_5(); + + const deploy = makeCsprTransferDeploy({ + chainName: 'casper', + recipientPublicKeyHex: + '0202f5a92ab6da536e7b1a351406f3744224bec85d7acbab1497b65de48a1a707b64', + senderPublicKeyHex: + '0202f5a92ab6da536e7b1a351406f3744224bec85d7acbab1497b65de48a1a707b64', + transferAmount: '25000000000' + }); + + expect(deploy.session.bytes().toString()).to.be.equal( + tx + .getDeploy() + ?.session.bytes() + .toString() + ); + }); + + it('should create CEP-18 transfer Deploy', () => { + const tx = new ContractCallBuilder() + .from( + PublicKey.fromHex( + '0202f5a92ab6da536e7b1a351406f3744224bec85d7acbab1497b65de48a1a707b64' + ) + ) + .byPackageHash( + 'f5e3729b502597fdd7be9ecedb6f73e4530f5e8a4c809f269d757677cbe49b78' + ) + .entryPoint('transfer') + .runtimeArgs( + Args.fromMap({ + recipient: CLValue.newCLKey( + Key.createByType( + PublicKey.fromHex( + '0202f5a92ab6da536e7b1a351406f3744224bec85d7acbab1497b65de48a1a707b64' + ) + .accountHash() + .toPrefixedString(), + KeyTypeID.Account + ) + ), + amount: CLValueUInt256.newCLUInt256('1000000000') + }) + ) + .payment(2_000000000) + .chainName('casper') + .buildFor1_5(); + + const deploy = makeCep18TransferDeploy({ + chainName: 'casper', + contractPackageHash: + 'f5e3729b502597fdd7be9ecedb6f73e4530f5e8a4c809f269d757677cbe49b78', + paymentAmount: '2000000000', + recipientPublicKeyHex: + '0202f5a92ab6da536e7b1a351406f3744224bec85d7acbab1497b65de48a1a707b64', + senderPublicKeyHex: + '0202f5a92ab6da536e7b1a351406f3744224bec85d7acbab1497b65de48a1a707b64', + transferAmount: '1000000000' + }); + + expect(deploy.session.bytes().toString()).to.be.equal( + tx + .getDeploy() + ?.session.bytes() + .toString() + ); + }); + + it('should create Auction Delegation Deploy', () => { + const tx = new NativeDelegateBuilder() + .from( + PublicKey.fromHex( + '0202f5a92ab6da536e7b1a351406f3744224bec85d7acbab1497b65de48a1a707b64' + ) + ) + .validator( + PublicKey.fromHex( + '0202f5a92ab6da536e7b1a351406f3744224bec85d7acbab1497b65de48a1a707b64' + ) + ) + .amount('100000000000') + .payment(2000000000) + .chainName('casper') + .buildFor1_5(); + + const deploy = makeAuctionManagerDeploy({ + amount: '100000000000', + chainName: CasperNetworkName.Mainnet, + contractEntryPoint: AuctionManagerEntryPoint.delegate, + delegatorPublicKeyHex: + '0202f5a92ab6da536e7b1a351406f3744224bec85d7acbab1497b65de48a1a707b64', + paymentAmount: '2000000000', + validatorPublicKeyHex: + '0202f5a92ab6da536e7b1a351406f3744224bec85d7acbab1497b65de48a1a707b64' + }); + + expect(deploy.session.bytes().toString()).to.be.equal( + tx + .getDeploy() + ?.session.bytes() + .toString() + ); + }); +}); diff --git a/src/types/TransactionBuilder.ts b/src/types/TransactionBuilder.ts index a6bed25d..72fbae2f 100644 --- a/src/types/TransactionBuilder.ts +++ b/src/types/TransactionBuilder.ts @@ -18,7 +18,7 @@ import { import { TransactionScheduling } from './TransactionScheduling'; import { Args } from './Args'; import { PublicKey } from './keypair'; -import { AccountHash, Hash } from './key'; +import { AccountHash, ContractHash, Hash } from './key'; import { Transaction, TransactionV1 } from './Transaction'; import { TransactionV1Payload } from './TransactionV1Payload'; import { Duration, Timestamp } from './Time'; @@ -31,6 +31,17 @@ import { CLValueUInt64, CLValueUInt8 } from './clvalue'; +import { + ExecutableDeployItem, + StoredContractByHash, + StoredContractByName, + StoredVersionedContractByHash, + StoredVersionedContractByName, + TransferDeployItem +} from './ExecutableDeployItem'; +import { Deploy, DeployHeader } from './Deploy'; +import { AuctionManagerContractHashMap } from '../utils'; +import { AuctionManagerEntryPoint, CasperNetworkName } from '../@types'; /** * Abstract base class for building Transaction V1 instances. @@ -101,6 +112,25 @@ abstract class TransactionBuilder> { return (this as unknown) as T; } + protected _getDefaultDeployHeader(): DeployHeader { + const deployHeader = DeployHeader.default(); + deployHeader.account = this._initiatorAddr.publicKey; + deployHeader.chainName = this._chainName; + deployHeader.ttl = this._ttl; + + return deployHeader; + } + + protected _getStandardPayment(): ExecutableDeployItem { + if (!this._pricingMode.paymentLimited?.paymentAmount) { + throw new Error('PaymentAmount is not specified'); + } + + return ExecutableDeployItem.standardPayment( + this._pricingMode.paymentLimited.paymentAmount.toString() + ); + } + /** * Builds and returns the Transaction instance. */ @@ -129,7 +159,9 @@ export class NativeTransferBuilder extends TransactionBuilder< NativeTransferBuilder > { private _target!: CLValue; + private _publicKey: PublicKey; private _amount: CLValue = CLValueUInt512.newCLUInt512('0'); + private _amountRow: BigNumber | string = '0'; private _idTransfer?: number; constructor() { @@ -144,6 +176,7 @@ export class NativeTransferBuilder extends TransactionBuilder< * Sets the target public key for the transfer. */ public target(publicKey: PublicKey): NativeTransferBuilder { + this._publicKey = publicKey; this._target = CLValue.newCLPublicKey(publicKey); return this; } @@ -160,6 +193,7 @@ export class NativeTransferBuilder extends TransactionBuilder< * Sets the amount to transfer. */ public amount(amount: BigNumber | string): NativeTransferBuilder { + this._amountRow = amount; this._amount = CLValueUInt512.newCLUInt512(amount); return this; } @@ -191,6 +225,29 @@ export class NativeTransferBuilder extends TransactionBuilder< this._runtimeArgs = runtimeArgs; return super.build(); } + + /** + * Builds and returns the Native Transfer transaction. + */ + public buildFor1_5(): Transaction { + const session = new ExecutableDeployItem(); + session.transfer = TransferDeployItem.newTransfer( + this._amountRow, + this._publicKey, + undefined, + this._idTransfer + ); + + const payment = ExecutableDeployItem.standardPayment('100000000'); + + const deploy = Deploy.makeDeploy( + this._getDefaultDeployHeader(), + payment, + session + ); + + return Transaction.fromDeploy(deploy); + } } export class NativeAddBidBuilder extends TransactionBuilder< @@ -278,6 +335,57 @@ export class NativeAddBidBuilder extends TransactionBuilder< return super.build(); } + + public buildFor1_5(): Transaction { + if (!this._initiatorAddr.publicKey) { + throw new Error('Initiator addr is not specified'); + } + + const runtimeArgs = Args.fromMap({}); + + runtimeArgs.insert('public_key', this._validator); + runtimeArgs.insert('amount', this._amount); + runtimeArgs.insert('delegation_rate', this._delegationRate); + + if (this._minimumDelegationAmount) { + runtimeArgs.insert( + 'minimum_delegation_amount', + this._minimumDelegationAmount + ); + } + + if (this._maximumDelegationAmount) { + runtimeArgs.insert( + 'maximum_delegation_amount', + this._maximumDelegationAmount + ); + } + + if (this._reservedSlots) { + runtimeArgs.insert('reserved_slots', this._reservedSlots); + } + + this._runtimeArgs = runtimeArgs; + + const contractHash = + AuctionManagerContractHashMap[this._chainName as CasperNetworkName] ?? + AuctionManagerContractHashMap.casper; + + const session = new ExecutableDeployItem(); + session.storedContractByHash = new StoredContractByHash( + ContractHash.newContract(contractHash), + AuctionManagerEntryPoint.addBid, + runtimeArgs + ); + + const deploy = Deploy.makeDeploy( + this._getDefaultDeployHeader(), + this._getStandardPayment(), + session + ); + + return Transaction.fromDeploy(deploy); + } } export class NativeWithdrawBidBuilder extends TransactionBuilder< @@ -312,6 +420,32 @@ export class NativeWithdrawBidBuilder extends TransactionBuilder< return super.build(); } + + public buildFor1_5(): Transaction { + this._runtimeArgs = Args.fromMap({ + public_key: this._validator, + amount: this._amount + }); + + const contractHash = + AuctionManagerContractHashMap[this._chainName as CasperNetworkName] ?? + AuctionManagerContractHashMap.casper; + + const session = new ExecutableDeployItem(); + session.storedContractByHash = new StoredContractByHash( + ContractHash.newContract(contractHash), + AuctionManagerEntryPoint.withdrawBid, + this._runtimeArgs + ); + + const deploy = Deploy.makeDeploy( + this._getDefaultDeployHeader(), + this._getStandardPayment(), + session + ); + + return Transaction.fromDeploy(deploy); + } } export class NativeDelegateBuilder extends TransactionBuilder< @@ -351,6 +485,35 @@ export class NativeDelegateBuilder extends TransactionBuilder< return super.build(); } + + public buildFor1_5(): Transaction { + if (!this._initiatorAddr.publicKey) { + throw new Error('Initiator addr is not specified'); + } + + const contractHash = + AuctionManagerContractHashMap[this._chainName as CasperNetworkName] ?? + AuctionManagerContractHashMap.casper; + + const session = new ExecutableDeployItem(); + session.storedContractByHash = new StoredContractByHash( + ContractHash.newContract(contractHash), + AuctionManagerEntryPoint.delegate, + Args.fromMap({ + validator: this._validator, + delegator: CLValue.newCLPublicKey(this._initiatorAddr.publicKey), + amount: this._amount + }) + ); + + const deploy = Deploy.makeDeploy( + this._getDefaultDeployHeader(), + this._getStandardPayment(), + session + ); + + return Transaction.fromDeploy(deploy); + } } export class NativeUndelegateBuilder extends TransactionBuilder< @@ -390,6 +553,35 @@ export class NativeUndelegateBuilder extends TransactionBuilder< return super.build(); } + + public buildFor1_5(): Transaction { + if (!this._initiatorAddr.publicKey) { + throw new Error('Initiator addr is not specified'); + } + + const contractHash = + AuctionManagerContractHashMap[this._chainName as CasperNetworkName] ?? + AuctionManagerContractHashMap.casper; + + const session = new ExecutableDeployItem(); + session.storedContractByHash = new StoredContractByHash( + ContractHash.newContract(contractHash), + AuctionManagerEntryPoint.undelegate, + Args.fromMap({ + validator: this._validator, + delegator: CLValue.newCLPublicKey(this._initiatorAddr.publicKey), + amount: this._amount + }) + ); + + const deploy = Deploy.makeDeploy( + this._getDefaultDeployHeader(), + this._getStandardPayment(), + session + ); + + return Transaction.fromDeploy(deploy); + } } export class NativeRedelegateBuilder extends TransactionBuilder< @@ -436,6 +628,36 @@ export class NativeRedelegateBuilder extends TransactionBuilder< return super.build(); } + + public buildFor1_5(): Transaction { + if (!this._initiatorAddr.publicKey) { + throw new Error('Initiator addr is not specified'); + } + + const contractHash = + AuctionManagerContractHashMap[this._chainName as CasperNetworkName] ?? + AuctionManagerContractHashMap.casper; + + const session = new ExecutableDeployItem(); + session.storedContractByHash = new StoredContractByHash( + ContractHash.newContract(contractHash), + AuctionManagerEntryPoint.redelegate, + Args.fromMap({ + validator: this._validator, + new_validator: this._newValidator, + delegator: CLValue.newCLPublicKey(this._initiatorAddr.publicKey), + amount: this._amount + }) + ); + + const deploy = Deploy.makeDeploy( + this._getDefaultDeployHeader(), + this._getStandardPayment(), + session + ); + + return Transaction.fromDeploy(deploy); + } } export class NativeActivateBidBuilder extends TransactionBuilder< @@ -463,6 +685,31 @@ export class NativeActivateBidBuilder extends TransactionBuilder< return super.build(); } + + public buildFor1_5(): Transaction { + this._runtimeArgs = Args.fromMap({ + validator: this._validator + }); + + const contractHash = + AuctionManagerContractHashMap[this._chainName as CasperNetworkName] ?? + AuctionManagerContractHashMap.casper; + + const session = new ExecutableDeployItem(); + session.storedContractByHash = new StoredContractByHash( + ContractHash.newContract(contractHash), + AuctionManagerEntryPoint.activateBid, + this._runtimeArgs + ); + + const deploy = Deploy.makeDeploy( + this._getDefaultDeployHeader(), + this._getStandardPayment(), + session + ); + + return Transaction.fromDeploy(deploy); + } } export class NativeChangeBidPublicKeyBuilder extends TransactionBuilder< @@ -508,9 +755,12 @@ export class ContractCallBuilder extends TransactionBuilder< super(); } + private _transactionInvocationTarget: TransactionInvocationTarget; + public byHash(contractHash: string): ContractCallBuilder { const invocationTarget = new TransactionInvocationTarget(); invocationTarget.byHash = Hash.fromHex(contractHash); + this._transactionInvocationTarget = invocationTarget; const storedTarget = new StoredTarget(); storedTarget.id = invocationTarget; @@ -523,6 +773,7 @@ export class ContractCallBuilder extends TransactionBuilder< public byName(name: string): ContractCallBuilder { const invocationTarget = new TransactionInvocationTarget(); invocationTarget.byName = name; + this._transactionInvocationTarget = invocationTarget; const storedTarget = new StoredTarget(); storedTarget.id = invocationTarget; @@ -541,6 +792,7 @@ export class ContractCallBuilder extends TransactionBuilder< packageHashInvocationTarget.version = version; const transactionInvocationTarget = new TransactionInvocationTarget(); transactionInvocationTarget.byPackageHash = packageHashInvocationTarget; + this._transactionInvocationTarget = transactionInvocationTarget; const storedTarget = new StoredTarget(); @@ -557,6 +809,7 @@ export class ContractCallBuilder extends TransactionBuilder< packageNameInvocationTarget.version = version; const transactionInvocationTarget = new TransactionInvocationTarget(); transactionInvocationTarget.byPackageName = packageNameInvocationTarget; + this._transactionInvocationTarget = transactionInvocationTarget; const storedTarget = new StoredTarget(); @@ -580,6 +833,54 @@ export class ContractCallBuilder extends TransactionBuilder< this._runtimeArgs = args; return this; } + + public buildFor1_5(): Transaction { + if (!this._entryPoint.customEntryPoint) { + throw new Error('EntryPoint is not specified'); + } + + const session = new ExecutableDeployItem(); + + if (this._transactionInvocationTarget.byHash) { + session.storedContractByHash = new StoredContractByHash( + ContractHash.newContract( + this._transactionInvocationTarget.byHash.toHex() + ), + this._entryPoint.customEntryPoint, + this._runtimeArgs + ); + } else if (this._transactionInvocationTarget.byPackageHash) { + session.storedVersionedContractByHash = new StoredVersionedContractByHash( + ContractHash.newContract( + this._transactionInvocationTarget.byPackageHash.addr.toHex() + ), + this._entryPoint.customEntryPoint, + this._runtimeArgs, + this._transactionInvocationTarget.byPackageHash.version + ); + } else if (this._transactionInvocationTarget.byName) { + session.storedContractByName = new StoredContractByName( + this._transactionInvocationTarget.byName, + this._entryPoint.customEntryPoint, + this._runtimeArgs + ); + } else if (this._transactionInvocationTarget.byPackageName) { + session.storedVersionedContractByName = new StoredVersionedContractByName( + this._transactionInvocationTarget.byPackageName.name, + this._entryPoint.customEntryPoint, + this._runtimeArgs, + this._transactionInvocationTarget.byPackageName.version + ); + } + + const deploy = Deploy.makeDeploy( + this._getDefaultDeployHeader(), + this._getStandardPayment(), + session + ); + + return Transaction.fromDeploy(deploy); + } } export class SessionBuilder extends TransactionBuilder { @@ -619,4 +920,23 @@ export class SessionBuilder extends TransactionBuilder { this._runtimeArgs = args; return this; } + + public buildFor1_5(): Transaction { + if (!this._invocationTarget.session?.moduleBytes) { + throw new Error('EntryPoint is not specified'); + } + + const session = ExecutableDeployItem.newModuleBytes( + this._invocationTarget.session.moduleBytes, + this._runtimeArgs + ); + + const deploy = Deploy.makeDeploy( + this._getDefaultDeployHeader(), + this._getStandardPayment(), + session + ); + + return Transaction.fromDeploy(deploy); + } } diff --git a/src/utils/auction-manager.ts b/src/utils/auction-manager.ts index 41b86fa7..85edcf39 100644 --- a/src/utils/auction-manager.ts +++ b/src/utils/auction-manager.ts @@ -15,7 +15,7 @@ import { AuctionManagerEntryPoint, CasperNetworkName } from '../@types'; import { AuctionManagerContractHashMap } from './constants'; export interface IMakeAuctionManagerDeployParams { - contractEntryPoint: AuctionManagerEntryPoint; + contractEntryPoint: AuctionManagerEntryPoint.delegate | AuctionManagerEntryPoint.undelegate | AuctionManagerEntryPoint.redelegate; delegatorPublicKeyHex: string; validatorPublicKeyHex: string; newValidatorPublicKeyHex?: string;