From 1ce811882706589655b4f80128720f3ad34631e9 Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Mon, 16 Dec 2024 11:32:01 +0100 Subject: [PATCH 1/9] Improve TransformKind transformation parsing --- src/types/Transform.ts | 46 ++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/src/types/Transform.ts b/src/types/Transform.ts index 5343f2e5..d35028e8 100644 --- a/src/types/Transform.ts +++ b/src/types/Transform.ts @@ -72,7 +72,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteTransfer, otherwise `false`. */ public isWriteTransfer(): boolean { - return this.data.includes?.('WriteTransfer') ?? false; + return this.isTransformation('WriteTransfer'); } /** @@ -81,7 +81,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteAccount, otherwise `false`. */ public isWriteAccount(): boolean { - return this.data.includes?.('WriteAccount') ?? false; + return this.isTransformation('WriteAccount'); } /** @@ -90,7 +90,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteContract, otherwise `false`. */ public isWriteContract(): boolean { - return this.data === '"WriteContract"'; + return this.isTransformation('WriteContract'); } /** @@ -99,7 +99,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteWithdraw, otherwise `false`. */ public isWriteWithdraw(): boolean { - return this.data.includes?.('WriteWithdraw') ?? false; + return this.isTransformation('WriteWithdraw'); } /** @@ -108,7 +108,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteUnbonding, otherwise `false`. */ public isWriteUnbonding(): boolean { - return this.data.includes?.('WriteUnbonding') ?? false; + return this.isTransformation('WriteUnbonding'); } /** @@ -117,7 +117,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteCLValue, otherwise `false`. */ public isWriteCLValue(): boolean { - return this.data.includes?.('CLValue') ?? false; + return this.isTransformation('CLValue'); } /** @@ -126,7 +126,7 @@ export class TransformKind { * @returns `true` if the transformation is a WritePackage, otherwise `false`. */ public isWritePackage(): boolean { - return this.data.includes?.('"Package"') ?? false; + return this.isTransformation('Package'); } /** @@ -135,7 +135,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteAddressableEntity, otherwise `false`. */ public isWriteAddressableEntity(): boolean { - return this.data.includes?.('"AddressableEntity"') ?? false; + return this.isTransformation('AddressableEntity'); } /** @@ -144,7 +144,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteBidKind, otherwise `false`. */ public isWriteBidKind(): boolean { - return this.data.includes?.('"BidKind"') ?? false; + return this.isTransformation('BidKind'); } /** @@ -153,7 +153,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteNamedKey, otherwise `false`. */ public isWriteNamedKey(): boolean { - return this.data.includes?.('"NamedKey"') ?? false; + return this.isTransformation('NamedKey'); } /** @@ -162,7 +162,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteMessage, otherwise `false`. */ public isWriteMessage(): boolean { - return this.data.includes?.('"Message"') ?? false; + return this.isTransformation('Message'); } /** @@ -171,7 +171,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteMessageTopic, otherwise `false`. */ public isWriteMessageTopic(): boolean { - return this.data.includes?.('"MessageTopic"') ?? false; + return this.isTransformation('MessageTopic'); } /** @@ -180,7 +180,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteBid, otherwise `false`. */ public isWriteBid(): boolean { - return this.data.includes?.('WriteBid') ?? false; + return this.isTransformation('WriteBid'); } /** @@ -189,7 +189,7 @@ export class TransformKind { * @returns `true` if the transformation is AddUInt512, otherwise `false`. */ public isAddUint512(): boolean { - return this.data.includes?.('AddUInt512') ?? false; + return this.isTransformation('AddUInt512'); } /** @@ -198,7 +198,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteDeployInfo, otherwise `false`. */ public isWriteDeployInfo(): boolean { - return this.data.includes?.('WriteDeployInfo') ?? false; + return this.isTransformation('WriteDeployInfo'); } /** @@ -398,6 +398,22 @@ export class TransformKind { return jsonRes2.Write?.CLValue; } + + /** + * Checks if `TransformKind` has the transformation specified by name. + * + * @param `name` - transformation name (aka WriteTransfer) + * @returns `true` if the transformation is a WriteTransfer, otherwise `false`. + */ + public isTransformation(name: string): boolean { + if (typeof this.data === 'string') { + return this.data.includes(name); + } else if (typeof this.data === 'object') { + return Object.keys(this.data).some(key => key.includes(name)); + } + + return false; + } } /** From 77e646349a527a2bf541d0d73879772f01f56be4 Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Mon, 16 Dec 2024 11:32:35 +0100 Subject: [PATCH 2/9] Remove unused Effects class --- src/types/ExecutionResult.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/types/ExecutionResult.ts b/src/types/ExecutionResult.ts index 3558daf2..f446e7f6 100644 --- a/src/types/ExecutionResult.ts +++ b/src/types/ExecutionResult.ts @@ -42,13 +42,6 @@ export class Operation { public kind: string; } -/** - * A collection of transformations applied during a transaction. - * A log of all transforms produced during execution, used only in 2.0+ Network - */ -@jsonObject -export class Effects extends Array {} - /** * Represents the effect of a transaction, including the operations and transformations. */ From 2f8b4e647fbec9f30df6f90368d8b499fe606b35 Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Mon, 16 Dec 2024 11:33:12 +0100 Subject: [PATCH 3/9] Add Args.getByName getter --- src/types/Args.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/types/Args.ts b/src/types/Args.ts index 277a4ec5..4d7b317a 100644 --- a/src/types/Args.ts +++ b/src/types/Args.ts @@ -127,6 +127,10 @@ export class Args { }) public args: Map; + public getByName(argName: string): CLValue | undefined { + return this.args.get(argName); + } + /** * Creates an instance of `Args` from a map of arguments. * @param args - A map containing argument names as keys and `CLValue` instances as values. From 2472eda9673f69c5d7382379a75a5670dc3bea25 Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Mon, 16 Dec 2024 11:33:53 +0100 Subject: [PATCH 4/9] Add initial list values to CLValueList.newCLList --- src/types/clvalue/List.ts | 5 +++-- src/types/clvalue/Tuple3.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/types/clvalue/List.ts b/src/types/clvalue/List.ts index 8b9260cc..0f86b516 100644 --- a/src/types/clvalue/List.ts +++ b/src/types/clvalue/List.ts @@ -127,12 +127,13 @@ export class CLValueList { /** * Creates a new CLValue instance with a List value. * @param elementType - The CLType for the elements of the list. + * @param elements - Optional array of CLValues to initialize the list with. * @returns A new CLValue instance containing CLTypeList and a CLValueList. */ - public static newCLList(elementType: CLType): CLValue { + public static newCLList(elementType: CLType, elements: CLValue[] = []): CLValue { const listType = new CLTypeList(elementType); const clValue = new CLValue(listType); - clValue.list = new CLValueList(listType); + clValue.list = new CLValueList(listType, elements); return clValue; } diff --git a/src/types/clvalue/Tuple3.ts b/src/types/clvalue/Tuple3.ts index 75084622..df03d676 100644 --- a/src/types/clvalue/Tuple3.ts +++ b/src/types/clvalue/Tuple3.ts @@ -54,7 +54,7 @@ export class CLValueTuple3 { * Retrieves the values of the tuple as an array. * @returns An array containing the three CLValues of the tuple. */ - public getValue(): [CLValue, CLValue, CLValue] { + public value(): [CLValue, CLValue, CLValue] { return [this.inner1, this.inner2, this.inner3]; } From 2591822cc374b374ebb2023095ef1c85482cb882 Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Mon, 16 Dec 2024 11:34:54 +0100 Subject: [PATCH 5/9] Rename Key's TypeID to KeyTypeID --- src/types/key/Key.ts | 294 +++++++++++++++++++++---------------------- 1 file changed, 147 insertions(+), 147 deletions(-) diff --git a/src/types/key/Key.ts b/src/types/key/Key.ts index 6e64b2aa..cf0b6416 100644 --- a/src/types/key/Key.ts +++ b/src/types/key/Key.ts @@ -52,7 +52,7 @@ export enum PrefixName { /** * Enum representing different types of blockchain key types used in the system. */ -const enum TypeID { +export const enum KeyTypeID { Account = 0, Hash, URef, @@ -101,54 +101,54 @@ export enum KeyTypeName { } /** - * Mapping of key type names to their corresponding TypeID values. + * Mapping of key type names to their corresponding KeyTypeID values. */ -export const typeIDbyNames = new Map([ - [KeyTypeName.Account, TypeID.Account], - [KeyTypeName.Hash, TypeID.Hash], - [KeyTypeName.URef, TypeID.URef], - [KeyTypeName.Transfer, TypeID.Transfer], - [KeyTypeName.Deploy, TypeID.DeployInfo], - [KeyTypeName.Era, TypeID.EraId], - [KeyTypeName.Bid, TypeID.Bid], - [KeyTypeName.Balance, TypeID.Balance], - [KeyTypeName.Withdraw, TypeID.Withdraw], - [KeyTypeName.Dictionary, TypeID.Dictionary], - [KeyTypeName.SystemContractRegistry, TypeID.SystemContractRegistry], - [KeyTypeName.EraSummary, TypeID.EraSummary], - [KeyTypeName.Unbond, TypeID.Unbond], - [KeyTypeName.ChainspecRegistry, TypeID.ChainspecRegistry], - [KeyTypeName.ChecksumRegistry, TypeID.ChecksumRegistry] +export const typeIDbyNames = new Map([ + [KeyTypeName.Account, KeyTypeID.Account], + [KeyTypeName.Hash, KeyTypeID.Hash], + [KeyTypeName.URef, KeyTypeID.URef], + [KeyTypeName.Transfer, KeyTypeID.Transfer], + [KeyTypeName.Deploy, KeyTypeID.DeployInfo], + [KeyTypeName.Era, KeyTypeID.EraId], + [KeyTypeName.Bid, KeyTypeID.Bid], + [KeyTypeName.Balance, KeyTypeID.Balance], + [KeyTypeName.Withdraw, KeyTypeID.Withdraw], + [KeyTypeName.Dictionary, KeyTypeID.Dictionary], + [KeyTypeName.SystemContractRegistry, KeyTypeID.SystemContractRegistry], + [KeyTypeName.EraSummary, KeyTypeID.EraSummary], + [KeyTypeName.Unbond, KeyTypeID.Unbond], + [KeyTypeName.ChainspecRegistry, KeyTypeID.ChainspecRegistry], + [KeyTypeName.ChecksumRegistry, KeyTypeID.ChecksumRegistry] ]); /** - * Mapping of blockchain key prefixes to their corresponding TypeID values. + * Mapping of blockchain key prefixes to their corresponding KeyKeyTypeID values. */ -export const keyIDbyPrefix = new Map([ - [PrefixName.Account, TypeID.Account], - [PrefixName.Hash, TypeID.Hash], - [PrefixName.Transfer, TypeID.Transfer], - [PrefixName.URef, TypeID.URef], - [PrefixName.DeployInfo, TypeID.DeployInfo], - [PrefixName.EraId, TypeID.EraId], - [PrefixName.Bid, TypeID.Bid], - [PrefixName.Balance, TypeID.Balance], - [PrefixName.Withdraw, TypeID.Withdraw], - [PrefixName.Dictionary, TypeID.Dictionary], - [PrefixName.SystemContractRegistry, TypeID.SystemContractRegistry], - [PrefixName.EraSummary, TypeID.EraSummary], - [PrefixName.Unbond, TypeID.Unbond], - [PrefixName.ChainspecRegistry, TypeID.ChainspecRegistry], - [PrefixName.ChecksumRegistry, TypeID.ChecksumRegistry], - [PrefixName.BidAddr, TypeID.BidAddr], - [PrefixName.Package, TypeID.Package], - [PrefixName.Entity, TypeID.AddressableEntity], - [PrefixName.ByteCode, TypeID.ByteCode], - [PrefixName.Message, TypeID.Message], - [PrefixName.NamedKey, TypeID.NamedKey], - [PrefixName.BlockGlobal, TypeID.BlockGlobal], - [PrefixName.BalanceHold, TypeID.BalanceHold], - [PrefixName.EntryPoint, TypeID.EntryPoint] +export const keyIDbyPrefix = new Map([ + [PrefixName.Account, KeyTypeID.Account], + [PrefixName.Hash, KeyTypeID.Hash], + [PrefixName.Transfer, KeyTypeID.Transfer], + [PrefixName.URef, KeyTypeID.URef], + [PrefixName.DeployInfo, KeyTypeID.DeployInfo], + [PrefixName.EraId, KeyTypeID.EraId], + [PrefixName.Bid, KeyTypeID.Bid], + [PrefixName.Balance, KeyTypeID.Balance], + [PrefixName.Withdraw, KeyTypeID.Withdraw], + [PrefixName.Dictionary, KeyTypeID.Dictionary], + [PrefixName.SystemContractRegistry, KeyTypeID.SystemContractRegistry], + [PrefixName.EraSummary, KeyTypeID.EraSummary], + [PrefixName.Unbond, KeyTypeID.Unbond], + [PrefixName.ChainspecRegistry, KeyTypeID.ChainspecRegistry], + [PrefixName.ChecksumRegistry, KeyTypeID.ChecksumRegistry], + [PrefixName.BidAddr, KeyTypeID.BidAddr], + [PrefixName.Package, KeyTypeID.Package], + [PrefixName.Entity, KeyTypeID.AddressableEntity], + [PrefixName.ByteCode, KeyTypeID.ByteCode], + [PrefixName.Message, KeyTypeID.Message], + [PrefixName.NamedKey, KeyTypeID.NamedKey], + [PrefixName.BlockGlobal, KeyTypeID.BlockGlobal], + [PrefixName.BalanceHold, KeyTypeID.BalanceHold], + [PrefixName.EntryPoint, KeyTypeID.EntryPoint] ]); /** @@ -162,7 +162,7 @@ export const KEY_DEFAULT_BYTE_LENGTH = 32; @jsonObject export class Key { @jsonMember({ name: 'Type', constructor: Number }) - type: TypeID; + type: KeyTypeID; @jsonMember({ name: 'Account', @@ -316,56 +316,56 @@ export class Key { const typeBytes = new Uint8Array([this.type]); switch (this.type) { - case TypeID.Balance: + case KeyTypeID.Balance: return Key.concatBytes(typeBytes, this.balance?.toBytes()); - case TypeID.Bid: + case KeyTypeID.Bid: return Key.concatBytes(typeBytes, this.bid?.toBytes()); - case TypeID.Withdraw: + case KeyTypeID.Withdraw: return Key.concatBytes(typeBytes, this.withdraw?.toBytes()); - case TypeID.SystemContractRegistry: + case KeyTypeID.SystemContractRegistry: return Key.concatBytes( typeBytes, this.systemContactRegistry?.toBytes() ); - case TypeID.Unbond: + case KeyTypeID.Unbond: return Key.concatBytes(typeBytes, this.unbond?.toBytes()); - case TypeID.ChainspecRegistry: + case KeyTypeID.ChainspecRegistry: return Key.concatBytes(typeBytes, this.chainspecRegistry?.toBytes()); - case TypeID.ChecksumRegistry: + case KeyTypeID.ChecksumRegistry: return Key.concatBytes(typeBytes, this.checksumRegistry?.toBytes()); - case TypeID.EraSummary: + case KeyTypeID.EraSummary: return Key.concatBytes(typeBytes, this.eraSummary?.toBytes()); - case TypeID.Account: + case KeyTypeID.Account: return Key.concatBytes(typeBytes, this.account?.toBytes()); - case TypeID.Hash: + case KeyTypeID.Hash: return Key.concatBytes(typeBytes, this.hash?.toBytes()); - case TypeID.EraId: + case KeyTypeID.EraId: return Key.concatBytes(typeBytes, this.era?.toBytes()); - case TypeID.URef: + case KeyTypeID.URef: return Key.concatBytes(typeBytes, this.uRef?.data); - case TypeID.Transfer: + case KeyTypeID.Transfer: return Key.concatBytes(typeBytes, this.transfer?.toBytes()); - case TypeID.DeployInfo: + case KeyTypeID.DeployInfo: return Key.concatBytes(typeBytes, this.deploy?.toBytes()); - case TypeID.Dictionary: + case KeyTypeID.Dictionary: return Key.concatBytes(typeBytes, this.dictionary?.toBytes()); - case TypeID.BidAddr: + case KeyTypeID.BidAddr: return Key.concatBytes(typeBytes, this.bidAddr?.toBytes()); - case TypeID.Package: + case KeyTypeID.Package: return Key.concatBytes(typeBytes, this.package?.toBytes()); - case TypeID.AddressableEntity: + case KeyTypeID.AddressableEntity: return Key.concatBytes(typeBytes, this.addressableEntity?.toBytes()); - case TypeID.ByteCode: + case KeyTypeID.ByteCode: return Key.concatBytes(typeBytes, this.byteCode?.toBytes()); - case TypeID.Message: + case KeyTypeID.Message: return Key.concatBytes(typeBytes, this.message?.toBytes()); - case TypeID.NamedKey: + case KeyTypeID.NamedKey: return Key.concatBytes(typeBytes, this.namedKey?.toBytes()); - case TypeID.BlockGlobal: + case KeyTypeID.BlockGlobal: return Key.concatBytes(typeBytes, this.blockGlobal?.toBytes()); - case TypeID.BalanceHold: + case KeyTypeID.BalanceHold: return Key.concatBytes(typeBytes, this.balanceHold?.toBytes()); - case TypeID.EntryPoint: + case KeyTypeID.EntryPoint: return Key.concatBytes(typeBytes, this.entryPoint?.toBytes()); default: return new Uint8Array(); @@ -394,59 +394,59 @@ export class Key { */ toPrefixedString(): string { switch (this.type) { - case TypeID.Account: + case KeyTypeID.Account: return this.account!.toPrefixedString(); - case TypeID.Hash: + case KeyTypeID.Hash: return `${PrefixName.Hash}${this.hash?.toHex()}`; - case TypeID.EraId: + case KeyTypeID.EraId: return `${PrefixName.EraId}${this.era?.toString()}`; - case TypeID.URef: + case KeyTypeID.URef: return this.uRef!.toPrefixedString(); - case TypeID.Transfer: + case KeyTypeID.Transfer: return this.transfer!.toPrefixedString(); - case TypeID.DeployInfo: + case KeyTypeID.DeployInfo: return `${PrefixName.DeployInfo}${this.deploy!.toHex()}`; - case TypeID.Dictionary: + case KeyTypeID.Dictionary: return `${PrefixName.Dictionary}${this.dictionary!.toHex()}`; - case TypeID.Balance: + case KeyTypeID.Balance: return `${PrefixName.Balance}${this.balance!.toHex()}`; - case TypeID.Bid: + case KeyTypeID.Bid: return `${PrefixName.Bid}${this.bid!.toHex()}`; - case TypeID.Withdraw: + case KeyTypeID.Withdraw: return `${PrefixName.Withdraw}${this.withdraw!.toHex()}`; - case TypeID.SystemContractRegistry: + case KeyTypeID.SystemContractRegistry: return `${ PrefixName.SystemContractRegistry }${this.systemContactRegistry!.toHex()}`; - case TypeID.EraSummary: + case KeyTypeID.EraSummary: return `${PrefixName.EraSummary}${this.eraSummary!.toHex()}`; - case TypeID.Unbond: + case KeyTypeID.Unbond: return `${PrefixName.Unbond}${this.unbond!.toHex()}`; - case TypeID.ChainspecRegistry: + case KeyTypeID.ChainspecRegistry: return `${ PrefixName.ChainspecRegistry }${this.chainspecRegistry!.toHex()}`; - case TypeID.ChecksumRegistry: + case KeyTypeID.ChecksumRegistry: return `${ PrefixName.ChecksumRegistry }${this.checksumRegistry!.toHex()}`; - case TypeID.BidAddr: + case KeyTypeID.BidAddr: return this.bidAddr!.toPrefixedString(); - case TypeID.Package: + case KeyTypeID.Package: return `${PrefixName.Package}${this.package!.toHex()}`; - case TypeID.AddressableEntity: + case KeyTypeID.AddressableEntity: return this.addressableEntity!.toPrefixedString(); - case TypeID.ByteCode: + case KeyTypeID.ByteCode: return this.byteCode!.toPrefixedString(); - case TypeID.Message: + case KeyTypeID.Message: return this.message!.toPrefixedString(); - case TypeID.NamedKey: + case KeyTypeID.NamedKey: return this.namedKey!.toPrefixedString(); - case TypeID.BlockGlobal: + case KeyTypeID.BlockGlobal: return this.blockGlobal!.toPrefixedString(); - case TypeID.BalanceHold: + case KeyTypeID.BalanceHold: return this.balanceHold!.toPrefixedString(); - case TypeID.EntryPoint: + case KeyTypeID.EntryPoint: return this.entryPoint!.toPrefixedString(); default: return ''; @@ -468,44 +468,44 @@ export class Key { * @throws Error if deserialization fails. */ public static fromBytes(bytes: Uint8Array): IResultWithBytes { - const keyType = bytes[0] as TypeID; + const keyType = bytes[0] as KeyTypeID; const contentBytes = bytes.subarray(1); const result = new Key(); result.type = keyType; switch (keyType) { - case TypeID.Account: + case KeyTypeID.Account: const accountHash = Hash.fromBytes(contentBytes); result.account = new AccountHash(accountHash?.result); return { result, bytes: accountHash?.bytes }; - case TypeID.Hash: + case KeyTypeID.Hash: const hashParsed = Hash.fromBytes(contentBytes); result.hash = hashParsed?.result; return { result, bytes: hashParsed?.bytes }; - case TypeID.URef: + case KeyTypeID.URef: const uref = URef.fromBytes(contentBytes); result.uRef = uref?.result; return { result, bytes: uref?.bytes }; - case TypeID.Transfer: + case KeyTypeID.Transfer: const [transferBytes, remainder] = splitAt( KEY_DEFAULT_BYTE_LENGTH, contentBytes ); result.transfer = new TransferHash(transferBytes); return { result, bytes: remainder }; - case TypeID.DeployInfo: + case KeyTypeID.DeployInfo: const [deployBytes, deployRemainder] = splitAt( KEY_DEFAULT_BYTE_LENGTH, contentBytes ); result.deploy = Hash.fromBytes(deployBytes)?.result; return { result, bytes: deployRemainder }; - case TypeID.EraId: + case KeyTypeID.EraId: const [eraBytes, eraRemainder] = splitAt(64, contentBytes); result.era = Era.fromBytes(eraBytes); return { result, bytes: eraRemainder }; - case TypeID.Balance: + case KeyTypeID.Balance: const [balanceBytes, balanceRemainder] = splitAt( KEY_DEFAULT_BYTE_LENGTH, contentBytes @@ -513,7 +513,7 @@ export class Key { result.balance = Hash.fromBytes(balanceBytes)?.result; return { result, bytes: balanceRemainder }; - case TypeID.Bid: + case KeyTypeID.Bid: const [bidBytes, bidRemainder] = splitAt( KEY_DEFAULT_BYTE_LENGTH, contentBytes @@ -522,7 +522,7 @@ export class Key { result.bid = new AccountHash(bidHash); return { result, bytes: bidRemainder }; - case TypeID.Withdraw: + case KeyTypeID.Withdraw: const [withdrawBytes, withDrawRemainder] = splitAt( KEY_DEFAULT_BYTE_LENGTH, contentBytes @@ -530,104 +530,104 @@ export class Key { const withdrawHash = Hash.fromBytes(withdrawBytes)?.result; result.withdraw = new AccountHash(withdrawHash); return { result, bytes: withDrawRemainder }; - case TypeID.Dictionary: + case KeyTypeID.Dictionary: const [dictBytes, dictRemainder] = splitAt( KEY_DEFAULT_BYTE_LENGTH, contentBytes ); result.dictionary = Hash.fromBytes(dictBytes)?.result; return { result, bytes: dictRemainder }; - case TypeID.SystemContractRegistry: + case KeyTypeID.SystemContractRegistry: const [systemBytes, systenRemainder] = splitAt( KEY_DEFAULT_BYTE_LENGTH, contentBytes ); result.systemContactRegistry = Hash.fromBytes(systemBytes)?.result; return { result, bytes: systenRemainder }; - case TypeID.EraSummary: + case KeyTypeID.EraSummary: const [eraSummaryBytes, eraSummaryRemainder] = splitAt( KEY_DEFAULT_BYTE_LENGTH, contentBytes ); result.eraSummary = Hash.fromBytes(eraSummaryBytes)?.result; return { result, bytes: eraSummaryRemainder }; - case TypeID.Unbond: + case KeyTypeID.Unbond: const { result: unbondHash, bytes: unbondBytes } = Hash.fromBytes( contentBytes ); result.unbond = new AccountHash(unbondHash); return { result, bytes: unbondBytes }; - case TypeID.ChainspecRegistry: + case KeyTypeID.ChainspecRegistry: const [chainBytes, chainspecRegistryBytes] = splitAt( KEY_DEFAULT_BYTE_LENGTH, contentBytes ); result.chainspecRegistry = Hash.fromBytes(chainBytes)?.result; return { result, bytes: chainspecRegistryBytes }; - case TypeID.ChecksumRegistry: + case KeyTypeID.ChecksumRegistry: const [checksumBytes, checksumRegistry] = splitAt( KEY_DEFAULT_BYTE_LENGTH, contentBytes ); result.checksumRegistry = Hash.fromBytes(checksumBytes)?.result; return { result, bytes: checksumRegistry }; - case TypeID.BidAddr: + case KeyTypeID.BidAddr: const { result: bidAddr, bytes: bidAddrBytes } = BidAddr.fromBytes( contentBytes ); result.bidAddr = bidAddr; return { result, bytes: bidAddrBytes }; - case TypeID.Package: + case KeyTypeID.Package: const [packageBytes, packageBytesRemainder] = splitAt( KEY_DEFAULT_BYTE_LENGTH, contentBytes ); result.package = Hash.fromBytes(packageBytes)?.result; return { result, bytes: packageBytesRemainder }; - case TypeID.AddressableEntity: + case KeyTypeID.AddressableEntity: const { result: entityAddr, bytes: entityAddrBytes } = EntityAddr.fromBytes(contentBytes); result.addressableEntity = entityAddr; return { result, bytes: entityAddrBytes }; - case TypeID.ByteCode: + case KeyTypeID.ByteCode: const { result: byteCode, bytes: byteCodeBytes } = ByteCode.fromBytes( contentBytes ); result.byteCode = byteCode; return { result, bytes: byteCodeBytes }; - case TypeID.Message: + case KeyTypeID.Message: const { result: messageAddr, bytes: messageAddrBytes } = MessageAddr.fromBytes(contentBytes); result.message = messageAddr; return { result, bytes: messageAddrBytes }; - case TypeID.NamedKey: + case KeyTypeID.NamedKey: const { result: namedKey, bytes: namedKeyBytes } = NamedKeyAddr.fromBytes(contentBytes); result.namedKey = namedKey; return { result, bytes: namedKeyBytes }; - case TypeID.BlockGlobal: + case KeyTypeID.BlockGlobal: const { result: blockGlobal, bytes: blockGlobalBytes } = BlockGlobalAddr.fromBytes(contentBytes); result.blockGlobal = blockGlobal; return { result, bytes: blockGlobalBytes }; - case TypeID.BalanceHold: + case KeyTypeID.BalanceHold: const { result: balanceHold, bytes: balanceHoldBytes } = BalanceHoldAddr.fromBytes(contentBytes); result.balanceHold = balanceHold; return { result, bytes: balanceHoldBytes }; - case TypeID.EntryPoint: + case KeyTypeID.EntryPoint: const { result: entryPoint, bytes: entryPointBytes @@ -642,12 +642,12 @@ export class Key { /** * Finds the prefix name by matching the source string with a map of prefixes. * @param source - The string to check for a matching prefix. - * @param prefixes - The map of prefix names to TypeID. + * @param prefixes - The map of prefix names to KeyTypeID. * @returns The matching PrefixName or undefined if not found. */ static findPrefixByMap( source: string, - prefixes: Map + prefixes: Map ): PrefixName { let result: PrefixName = '' as PrefixName; @@ -685,110 +685,110 @@ export class Key { * @returns A new Key instance. * @throws Error if the type is not found or invalid. */ - static createByType(source: string, typeID: TypeID): Key { + static createByType(source: string, typeID: KeyTypeID): Key { const result = new Key(); result.type = typeID; switch (result.type) { - case TypeID.EraId: + case KeyTypeID.EraId: result.era = Era.fromJSON(source.replace(PrefixName.EraId, '')); break; - case TypeID.Hash: + case KeyTypeID.Hash: result.hash = Hash.fromHex(source.replace(PrefixName.Hash, '')); break; - case TypeID.URef: + case KeyTypeID.URef: result.uRef = URef.fromString(source); break; - case TypeID.Account: + case KeyTypeID.Account: result.account = AccountHash.fromString(source); break; - case TypeID.Transfer: + case KeyTypeID.Transfer: result.transfer = TransferHash.fromJSON(source); break; - case TypeID.DeployInfo: + case KeyTypeID.DeployInfo: result.deploy = Hash.fromHex(source.replace(PrefixName.DeployInfo, '')); break; - case TypeID.Balance: + case KeyTypeID.Balance: result.balance = Hash.fromHex(source.replace(PrefixName.Balance, '')); break; - case TypeID.Bid: + case KeyTypeID.Bid: result.bid = AccountHash.fromString(source.replace(PrefixName.Bid, '')); break; - case TypeID.Withdraw: + case KeyTypeID.Withdraw: result.withdraw = AccountHash.fromString( source.replace(PrefixName.Withdraw, '') ); break; - case TypeID.Dictionary: + case KeyTypeID.Dictionary: result.dictionary = Hash.fromHex( source.replace(PrefixName.Dictionary, '') ); break; - case TypeID.SystemContractRegistry: + case KeyTypeID.SystemContractRegistry: result.systemContactRegistry = Hash.fromHex( source.replace(PrefixName.SystemContractRegistry, '') ); break; - case TypeID.EraSummary: + case KeyTypeID.EraSummary: result.eraSummary = Hash.fromHex( source.replace(PrefixName.EraSummary, '') ); break; - case TypeID.Unbond: + case KeyTypeID.Unbond: result.unbond = AccountHash.fromString( source.replace(PrefixName.Unbond, '') ); break; - case TypeID.ChainspecRegistry: + case KeyTypeID.ChainspecRegistry: result.chainspecRegistry = Hash.fromHex( source.replace(PrefixName.ChainspecRegistry, '') ); break; - case TypeID.ChecksumRegistry: + case KeyTypeID.ChecksumRegistry: result.checksumRegistry = Hash.fromHex( source.replace(PrefixName.ChecksumRegistry, '') ); break; - case TypeID.BidAddr: + case KeyTypeID.BidAddr: result.bidAddr = BidAddr.fromHex( source.replace(PrefixName.BidAddr, '') ); break; - case TypeID.Package: + case KeyTypeID.Package: result.package = Hash.fromHex(source.replace(PrefixName.Package, '')); break; - case TypeID.AddressableEntity: + case KeyTypeID.AddressableEntity: result.addressableEntity = EntityAddr.fromPrefixedString( source.replace(PrefixName.AddressableEntity, '') ); break; - case TypeID.ByteCode: + case KeyTypeID.ByteCode: result.byteCode = ByteCode.fromJSON( source.replace(PrefixName.ByteCode, '') ); break; - case TypeID.Message: + case KeyTypeID.Message: result.message = MessageAddr.fromString( source.replace(PrefixName.Message, '') ); break; - case TypeID.NamedKey: + case KeyTypeID.NamedKey: result.namedKey = NamedKeyAddr.fromString( source.replace(PrefixName.NamedKey, '') ); break; - case TypeID.BlockGlobal: + case KeyTypeID.BlockGlobal: result.blockGlobal = BlockGlobalAddr.fromString( source.replace(PrefixName.BlockGlobal, '') ); break; - case TypeID.BalanceHold: + case KeyTypeID.BalanceHold: result.balanceHold = BalanceHoldAddr.fromString( source.replace(PrefixName.BalanceHold, '') ); break; - case TypeID.EntryPoint: + case KeyTypeID.EntryPoint: result.entryPoint = EntryPointAddr.fromString( source.replace(PrefixName.EntryPoint, '') ); @@ -832,7 +832,7 @@ export class Key { if (source.length === Hash.StringHashLen) { const defaultHash = Hash.fromHex(source); const result = new Key(); - result.type = TypeID.Hash; + result.type = KeyTypeID.Hash; result.hash = defaultHash; return result; } @@ -842,7 +842,7 @@ export class Key { } if (source.startsWith('00') && source.length === Hash.StringHashLen + 2) { - return Key.createByType(source.slice(2), TypeID.Account); + return Key.createByType(source.slice(2), KeyTypeID.Account); } const prefix = Key.findPrefixByMap(source, keyIDbyPrefix); From 91cfe66aa9110e85b0f988b55ea7fcf3510a7b69 Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Mon, 16 Dec 2024 11:37:20 +0100 Subject: [PATCH 6/9] Add PrivateKey.toBytes method --- src/types/keypair/PrivateKey.ts | 11 ++++++++--- src/types/keypair/ed25519/PrivateKey.ts | 24 +++++------------------ src/types/keypair/secp256k1/PrivateKey.ts | 24 +++++------------------ 3 files changed, 18 insertions(+), 41 deletions(-) diff --git a/src/types/keypair/PrivateKey.ts b/src/types/keypair/PrivateKey.ts index 27e2342f..e6235976 100644 --- a/src/types/keypair/PrivateKey.ts +++ b/src/types/keypair/PrivateKey.ts @@ -5,12 +5,13 @@ import { PrivateKey as Ed25519PrivateKey } from './ed25519/PrivateKey'; import { PrivateKey as Secp256k1PrivateKey } from './secp256k1/PrivateKey'; /** - * Interface representing the structure of a private key, with methods for - * obtaining the public key, signing messages, and exporting to PEM format. + * Interface representing the structure and methods of a private key, including + * functions to retrieve public key bytes, sign messages, and export to PEM format. */ -interface PrivateKeyInternal { +export interface PrivateKeyInternal { /** Retrieves the public key bytes. */ publicKeyBytes(): Promise; + toBytes(): Uint8Array; /** * Signs a message using the private key. @@ -57,6 +58,10 @@ export class PrivateKey { this.priv = priv; } + toBytes(): Uint8Array { + return this.priv.toBytes(); + } + /** * Gets the public key associated with this private key. * @returns The associated PublicKey instance. diff --git a/src/types/keypair/ed25519/PrivateKey.ts b/src/types/keypair/ed25519/PrivateKey.ts index 6679ff88..bfebf4a3 100644 --- a/src/types/keypair/ed25519/PrivateKey.ts +++ b/src/types/keypair/ed25519/PrivateKey.ts @@ -1,23 +1,5 @@ import * as ed25519 from '@noble/ed25519'; - -/** - * Interface representing the structure and methods of a private key, including - * functions to retrieve public key bytes, sign messages, and export to PEM format. - */ -interface PrivateKeyInternal { - /** Retrieves the public key bytes. */ - publicKeyBytes(): Promise; - - /** - * Signs a message using the private key. - * @param message - The message to sign. - * @returns A promise that resolves to the signature bytes. - */ - sign(message: Uint8Array): Promise; - - /** Converts the private key to PEM format. */ - toPem(): string; -} +import { PrivateKeyInternal } from "../PrivateKey"; /** * Represents an Ed25519 private key, supporting key generation, signing, and PEM encoding. @@ -55,6 +37,10 @@ export class PrivateKey implements PrivateKeyInternal { return ed25519.getPublicKey(this.key); } + toBytes(): Uint8Array { + return this.key; + } + /** * Signs a message using the private key. * @param message - The message to sign. diff --git a/src/types/keypair/secp256k1/PrivateKey.ts b/src/types/keypair/secp256k1/PrivateKey.ts index a158dac3..7c5a15ae 100644 --- a/src/types/keypair/secp256k1/PrivateKey.ts +++ b/src/types/keypair/secp256k1/PrivateKey.ts @@ -1,5 +1,6 @@ import * as secp256k1 from '@noble/secp256k1'; import { sha256 } from '@noble/hashes/sha256'; +import { PrivateKeyInternal } from "../PrivateKey"; /** PEM prefix for a private key. */ const PemPrivateKeyPrefix = '-----BEGIN PRIVATE KEY-----'; @@ -7,25 +8,6 @@ const PemPrivateKeyPrefix = '-----BEGIN PRIVATE KEY-----'; /** PEM suffix for a private key. */ const PemPrivateKeySuffix = '-----END PRIVATE KEY-----'; -/** - * Interface representing the structure and methods of a private key, including - * functions to retrieve public key bytes, sign messages, and export to PEM format. - */ -interface PrivateKeyInternal { - /** Retrieves the public key bytes. */ - publicKeyBytes(): Promise; - - /** - * Signs a message using the private key. - * @param message - The message to sign. - * @returns A promise resolving to the signature bytes. - */ - sign(message: Uint8Array): Promise; - - /** Converts the private key to PEM format. */ - toPem(): string; -} - /** * Represents a secp256k1 private key, supporting key generation, signing, and PEM encoding. * The class offers static methods to create instances from bytes, hex, and PEM formats. @@ -67,6 +49,10 @@ export class PrivateKey implements PrivateKeyInternal { return secp256k1.getPublicKey(this.key, true); } + toBytes(): Uint8Array { + return this.key; + } + /** * Signs a message using the private key. * The message is first hashed with SHA-256 before signing. From b11cb481e92ca6c75a70546b5f17dc80b4deede5 Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Mon, 16 Dec 2024 11:45:31 +0100 Subject: [PATCH 7/9] Fix ed25519 private key length validation --- src/types/keypair/ed25519/PrivateKey.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/types/keypair/ed25519/PrivateKey.ts b/src/types/keypair/ed25519/PrivateKey.ts index bfebf4a3..b3d74e48 100644 --- a/src/types/keypair/ed25519/PrivateKey.ts +++ b/src/types/keypair/ed25519/PrivateKey.ts @@ -58,7 +58,7 @@ export class PrivateKey implements PrivateKeyInternal { * @throws Error if the byte array length is not 64. */ static fromBytes(key: Uint8Array): PrivateKey { - if (key.length !== 64) { + if (key.length !== 32) { throw new Error(`Invalid key size: expected 64 bytes, got ${key.length}`); } return new PrivateKey(key); @@ -72,9 +72,9 @@ export class PrivateKey implements PrivateKeyInternal { * @throws Error if the hex string length is not 128 characters. */ static fromHex(keyHex: string): PrivateKey { - if (keyHex.length !== 128) { + if (keyHex.length !== 64) { throw new Error( - `Invalid hex string length: expected 128 characters, got ${keyHex.length}` + `Invalid hex string length: expected 64 characters, got ${keyHex.length}` ); } const keyBytes = Buffer.from(keyHex, 'hex'); From 97738010b93409f6d8610a50bc290bcce7c84069 Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Mon, 16 Dec 2024 11:57:41 +0100 Subject: [PATCH 8/9] Add makeCep18TransferDeploy and makeNftTransferDeploy utils --- README.md | 78 ++++++++++++++++ src/@types/common.ts | 5 + src/utils/auction-manager.ts | 9 +- src/utils/cep-18-transfer.ts | 93 +++++++++++++++++++ src/utils/cep-nft-transfer.ts | 168 ++++++++++++++++++++++++++++++++++ src/utils/cspr-transfer.ts | 8 +- src/utils/index.ts | 2 + 7 files changed, 357 insertions(+), 6 deletions(-) create mode 100644 src/utils/cep-18-transfer.ts create mode 100644 src/utils/cep-nft-transfer.ts diff --git a/README.md b/README.md index b111be09..7049d170 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ npm install casper-js-sdk --save - [Creating a legacy deploy](#creating-a-legacy-deploy) - [Creating and sending CSPR transfer deploy](#creating-and-sending-cspr-transfer-deploy) - [Creating and sending Auction manager deploy](#creating-and-sending-auction-manager-deploy) +- [Creating and sending CEP-18 transfer deploy](#creating-and-sending-cep-18-transfer-deploy) +- [Creating and sending NFT transfer deploy](#creating-and-sending-nft-transfer-deploy) ## Migration guides @@ -310,3 +312,79 @@ const result = await rpcClient.putDeploy(deploy); console.log(`Deploy Hash: ${result.deployHash}`); ``` + +### Creating and sending CEP-18 transfer deploy + +Example of how to construct a CEP-18 transfer deploy and push it to the network: + +```ts +import { + HttpHandler, + RpcClient, + KeyAlgorithm, + PrivateKey, + makeCep18TransferDeploy +} from 'casper-js-sdk'; + +// get private key fromHex, fromPem or generate it +const privateKey = await PrivateKey.fromHex( + 'privateKeyHex', + KeyAlgorithm.SECP256K1 // or KeyAlgorithm.ED25519, depends on your private key +); + +const deploy = await makeCep18TransferDeploy({ + contractHash: '0123456789asdfbcdef...', + senderPublicKeyHex: '0123456789asdfbcdef...', + recipientPublicKeyHex: '0123456789abcdef...', + transferAmount: '25000000000', // 25 CEP-18 with 9 decimals + paymentAmount: '3000000000' // 3 CSPR +}); + +await deploy.sign(privateKey); + +const rpcHandler = new HttpHandler('http://:7777/rpc'); +const rpcClient = new RpcClient(rpcHandler); + +const result = await rpcClient.putDeploy(deploy); + +console.log(`Deploy Hash: ${result.deployHash}`); +``` + +### Creating and sending NFT transfer deploy + +Example of how to construct a NFT transfer deploy and push it to the network: + +```ts +import { + HttpHandler, + RpcClient, + KeyAlgorithm, + PrivateKey, + makeNftTransferDeploy, + NFTTokenStandard +} from 'casper-js-sdk'; + +// get private key fromHex, fromPem or generate it +const privateKey = await PrivateKey.fromHex( + 'privateKeyHex', + KeyAlgorithm.SECP256K1 // or KeyAlgorithm.ED25519, depends on your private key +); + +const deploy = await makeNftTransferDeploy({ + nftStandard: NFTTokenStandard.CEP47, + contractPackageHash: '0123456789asdfbcdef...', + senderPublicKeyHex: '0123456789asdfbcdef...', + recipientPublicKeyHex: '0123456789abcdef...', + paymentAmount: '3000000000', // 3 CSPR + tokenId: 234 +}); + +await deploy.sign(privateKey); + +const rpcHandler = new HttpHandler('http://:7777/rpc'); +const rpcClient = new RpcClient(rpcHandler); + +const result = await rpcClient.putDeploy(deploy); + +console.log(`Deploy Hash: ${result.deployHash}`); +``` diff --git a/src/@types/common.ts b/src/@types/common.ts index 26658b6b..b772d500 100644 --- a/src/@types/common.ts +++ b/src/@types/common.ts @@ -8,3 +8,8 @@ export enum AuctionManagerEntryPoint { undelegate = 'undelegate', redelegate = 'redelegate' } + +export enum NFTTokenStandard { + CEP47 = 'CEP47', + CEP78 = 'CEP78' +} diff --git a/src/utils/auction-manager.ts b/src/utils/auction-manager.ts index 942e97e0..41b86fa7 100644 --- a/src/utils/auction-manager.ts +++ b/src/utils/auction-manager.ts @@ -26,13 +26,13 @@ export interface IMakeAuctionManagerDeployParams { } /** - * Creates a deploy for the Auction Manager contract. + * Creates a `Deploy` for the Auction Manager contract. * - * This function generates a deploy that interacts with the Auction Manager + * This function generates a `Deploy` that interacts with the Auction Manager * contract on the Casper network. It supports operations such as delegation, * un-delegation, and validator change management. * - * @param params - The parameters required to create the deploy. + * @param params - The parameters required to create the Auction Manager deploy. * @param params.contractEntryPoint - The entry point to invoke in the Auction Manager contract. * @param params.delegatorPublicKeyHex - The delegator's public key in hexadecimal format. * @param params.validatorPublicKeyHex - The validator's public key in hexadecimal format. @@ -46,6 +46,9 @@ export interface IMakeAuctionManagerDeployParams { * @param params.chainName - (Optional) The name of the Casper network chain - {CasperNetworkName}. * Must be either `'casper'` (mainnet) or `'casper-test'` (testnet). * Defaults to `'CasperNetworkName.Mainnet'` if not specified. + * @param params.ttl - (Optional) The time-to-live (TTL) for the `Deploy` in milliseconds. + * Specifies how long the `Deploy` is valid before it expires. + * Defaults 1800000 (30 minutes) * * @returns A deploy object that can be signed and sent to the network. * diff --git a/src/utils/cep-18-transfer.ts b/src/utils/cep-18-transfer.ts new file mode 100644 index 00000000..2a8daa2c --- /dev/null +++ b/src/utils/cep-18-transfer.ts @@ -0,0 +1,93 @@ +import { + Args, + CLValue, + CLValueUInt256, + ContractHash, + DEFAULT_DEPLOY_TTL, + Deploy, + DeployHeader, + Duration, + ExecutableDeployItem, Key, KeyTypeID, + PublicKey, + StoredContractByHash +} from '../types'; +import { CasperNetworkName } from '../@types'; + +export interface IMakeCep18TransferDeployParams { + contractHash: string, + senderPublicKeyHex: string; + recipientPublicKeyHex: string; + transferAmount: string; + paymentAmount: string, + chainName?: CasperNetworkName; + ttl?: number; +} + +/** + * 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.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. + * This value must be represented in its smallest unit (motes). + * For example, to transfer 2.5 CSPR, provide the value `2500000000` (2.5 * 10^9 motes). + * @param params.paymentAmount - The amount of CSPR to pay a network fee. + * This value must be represented in its smallest unit (motes). + * @param params.chainName - (Optional) The name of the Casper network chain - {CasperNetworkName}. + * Must be either `'casper'` (mainnet) or `'casper-test'` (testnet). + * Defaults to `'CasperNetworkName.Mainnet'` if not specified. + * @param params.ttl - (Optional) The time-to-live (TTL) for the `Deploy` in milliseconds. + * Specifies how long the `Deploy` is valid before it expires. + * Defaults 1800000 (30 minutes) + * @returns A promise that resolves to the created Deploy instance, ready to be sent to the Casper network. + * + * @example + * ```ts + * import { makeCsprTransferDeploy } from 'casper-js-sdk'; + * + * const deploy = await makeCep18TransferDeploy({ + * contractHash: '0123456789asdfbcdef...', + * senderPublicKeyHex: '0123456789asdfbcdef...', + * recipientPublicKeyHex: '0123456789abcdef...', + * transferAmount: '25000000000', // 25 CEP-18 with 9 decimals + * paymentAmount: '3000000000', // 3 CSPR + * }); + * + * console.log('Created Deploy:', deploy); + * ``` + */ +export const makeCep18TransferDeploy = ({ + contractHash, + senderPublicKeyHex, + recipientPublicKeyHex, + transferAmount, + paymentAmount, + chainName = CasperNetworkName.Mainnet, + ttl = DEFAULT_DEPLOY_TTL, +}: IMakeCep18TransferDeployParams): Deploy => { + const senderPublicKey = PublicKey.newPublicKey(senderPublicKeyHex); + const recipientPublicKey = PublicKey.newPublicKey(recipientPublicKeyHex); + + const session = new ExecutableDeployItem(); + + session.storedContractByHash = new StoredContractByHash( + ContractHash.newContract(contractHash), + 'transfer', + Args.fromMap({ + recipient: CLValue.newCLKey(Key.createByType(recipientPublicKey.accountHash().toPrefixedString(), KeyTypeID.Account)), + amount: CLValueUInt256.newCLUInt256(transferAmount) + }) + ); + + const payment = ExecutableDeployItem.standardPayment(paymentAmount); + + const deployHeader = DeployHeader.default(); + deployHeader.account = senderPublicKey; + deployHeader.chainName = chainName; + deployHeader.ttl = new Duration(ttl); + + return Deploy.makeDeploy(deployHeader, payment, session); +}; diff --git a/src/utils/cep-nft-transfer.ts b/src/utils/cep-nft-transfer.ts new file mode 100644 index 00000000..2dc69eb5 --- /dev/null +++ b/src/utils/cep-nft-transfer.ts @@ -0,0 +1,168 @@ +import { + Args, + CLTypeUInt256, + CLValue, + CLValueBool, + CLValueList, + CLValueUInt256, + CLValueUInt64, + ContractHash, + DEFAULT_DEPLOY_TTL, + Deploy, + DeployHeader, + Duration, + ExecutableDeployItem, Key, KeyTypeID, + PublicKey, + StoredVersionedContractByHash +} from '../types'; +import { CasperNetworkName, NFTTokenStandard } from '../@types'; + +export interface IMakeNftTransferDeployParams { + nftStandard: NFTTokenStandard; + contractPackageHash: string; + senderPublicKeyHex: string; + recipientPublicKeyHex: string; + paymentAmount: string; + chainName?: CasperNetworkName; + ttl?: number; + tokenId?: string; + tokenHash?: string; +} + +/** + * Creates a `Deploy` for transferring an NFT (Non-Fungible Token). + * This function constructs and returns a `Deploy` for transferring NFTs according to the specified parameters. + * + * @param params - The parameters required to create the NFT transfer deploy. + * @param params.nftStandard - The NFT standard being used (e.g., CEP-78, CEP-47). + * @param params.contractPackageHash - The hash of the contract package to interact with. + * @param params.senderPublicKeyHex - The sender's public key in hexadecimal format. + * @param params.recipientPublicKeyHex - The recipient's public key in hexadecimal format. + * @param params.paymentAmount - The payment amount for the transaction, specified in motes. + * @param params.chainName - The name of the Casper network chain (e.g., "casper", "casper-test"). Defaults to Mainnet. + * @param params.ttl - The time-to-live (TTL) for the deploy in milliseconds. Defaults to the constant `DEFAULT_DEPLOY_TTL`. + * @param params.tokenId - The ID of the token to transfer. Optional and used if the standard requires it. + * @param params.tokenHash - The hash of the token to transfer. Optional and used if the standard requires it. + * + * @returns A deploy object representing the NFT transfer operation. + * + * @example + * ```ts + * import { makeNftTransferDeploy, NFTTokenStandard } from 'casper-js-sdk'; + * + * const deploy = await makeNftTransferDeploy({ + * nftStandard: NFTTokenStandard.CEP47, + * contractPackageHash: '0123456789asdfbcdef...', + * senderPublicKeyHex: '0123456789asdfbcdef...', + * recipientPublicKeyHex: '0123456789abcdef...', + * paymentAmount: '3000000000', // 3 CSPR + * tokenId: 234, + * }); + * + * console.log('Created Deploy:', deploy); + * ``` + */ +export const makeNftTransferDeploy = ({ + nftStandard, + contractPackageHash, + senderPublicKeyHex, + recipientPublicKeyHex, + paymentAmount, + chainName = CasperNetworkName.Mainnet, + ttl = DEFAULT_DEPLOY_TTL, + tokenId, + tokenHash +}: IMakeNftTransferDeployParams): Deploy => { + const senderPublicKey = PublicKey.newPublicKey(senderPublicKeyHex); + + if (!(tokenId || tokenHash)) { + throw new Error('Specify either tokenId or tokenHash to make a transfer') + } + + let args: Args | null = null; + + if (nftStandard === NFTTokenStandard.CEP47) { + if (!tokenId) { + throw new Error('TokenId is required for CEP-47 transfer') + } + + args = getRuntimeArgsForCep47Transfer({ tokenId, recipientPublicKeyHex }) + } + + if (nftStandard === NFTTokenStandard.CEP78) { + args = getRuntimeArgsForCep78Transfer({ + tokenId, + tokenHash, + senderPublicKeyHex, + recipientPublicKeyHex + }); + } + + if (!args) { + throw new Error('Deploy arguments error. Check provided token data') + } + + const session = new ExecutableDeployItem(); + + session.storedVersionedContractByHash = new StoredVersionedContractByHash( + ContractHash.newContract(contractPackageHash), + 'transfer', + args + ); + + const payment = ExecutableDeployItem.standardPayment(paymentAmount); + + const deployHeader = DeployHeader.default(); + deployHeader.account = senderPublicKey; + deployHeader.chainName = chainName; + deployHeader.ttl = new Duration(ttl); + + return Deploy.makeDeploy(deployHeader, payment, session); +}; + +export const getRuntimeArgsForCep78Transfer = ({ + tokenHash, + tokenId, + recipientPublicKeyHex, + senderPublicKeyHex +}: Pick< + IMakeNftTransferDeployParams, + 'tokenId' | 'recipientPublicKeyHex' | 'tokenHash' | 'senderPublicKeyHex' +>) => { + const runtimeArgs = Args.fromMap({ + target_key: CLValue.newCLKey(Key.createByType(PublicKey.fromHex(recipientPublicKeyHex).accountHash().toPrefixedString(), KeyTypeID.Account)), + source_key: CLValue.newCLKey(Key.createByType(PublicKey.fromHex(senderPublicKeyHex).accountHash().toPrefixedString(), KeyTypeID.Account)) + }); + + if (tokenId) { + runtimeArgs.insert( + 'is_hash_identifier_mode', + CLValueBool.fromBoolean(false) + ); + runtimeArgs.insert('token_id', CLValueUInt64.newCLUint64(tokenId)); + } + + if (tokenHash) { + runtimeArgs.insert( + 'is_hash_identifier_mode', + CLValueBool.fromBoolean(true) + ); + runtimeArgs.insert('token_id', CLValueUInt64.newCLUint64(tokenHash)); + } + + return runtimeArgs; +}; + +export function getRuntimeArgsForCep47Transfer({ + tokenId, + recipientPublicKeyHex +}: Required< + Pick +>) { + return Args.fromMap({ + recipient: CLValue.newCLKey(Key.createByType(PublicKey.fromHex(recipientPublicKeyHex).accountHash().toPrefixedString(), KeyTypeID.Account)), + token_ids: CLValueList.newCLList(CLTypeUInt256, [ + CLValueUInt256.newCLUInt256(tokenId) + ]) + }); +} diff --git a/src/utils/cspr-transfer.ts b/src/utils/cspr-transfer.ts index 30531360..d49facb3 100644 --- a/src/utils/cspr-transfer.ts +++ b/src/utils/cspr-transfer.ts @@ -19,10 +19,9 @@ export interface IMakeCsprTransferDeployParams { } /** - * Creates a CSPR transfer deploy. + * Creates a CSPR transfer `Deploy`. * - * This function generates a deploy for transferring CSPR from one account to another. - * It utilizes the provided private key, recipient's public key, and transfer amount to build the deploy. + * This function generates a `Deploy` for transferring CSPR from one account to another. * * @param params - The parameters required to create the transfer deploy. * @param params.senderPublicKeyHex - The sender's public key in hexadecimal format. @@ -36,6 +35,9 @@ export interface IMakeCsprTransferDeployParams { * @param params.memo - (Optional) Tag/Memo (Comment/Note/Remark) * Most exchanges require a Tag/Memo for CSPR deposits to be credited correctly. * Make sure to provide the Tag/Memo if required. + * @param params.ttl - (Optional) The time-to-live (TTL) for the `Deploy` in milliseconds. + * Specifies how long the `Deploy` is valid before it expires. + * Defaults 1800000 (30 minutes) * * @returns A promise that resolves to the created Deploy instance, ready to be sent to the Casper network. * diff --git a/src/utils/index.ts b/src/utils/index.ts index 3241d68b..eb8e7604 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,3 +1,5 @@ export * from './cspr-transfer'; export * from './auction-manager'; export * from './constants'; +export * from './cep-18-transfer'; +export * from './cep-nft-transfer'; From 4efa3ff6cb6dee9faf5b964ab14f9140566d73bf Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Mon, 16 Dec 2024 12:05:36 +0100 Subject: [PATCH 9/9] Release 5.0.0-rc8 version --- CHANGELOG.md | 33 +++++++++++++++++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c3710ea..5cd46623 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,39 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Removed --> +## [5.0.0-rc8] - 2024-12-16 + +### Added + +- `makeCep18TransferDeploy` and `makeNftTransferDeploy` utils +- `PrivateKey.toBytes` method +- Rename Key's TypeID to KeyTypeID +- Add initial list values to `CLValueList.newCLList` +- `Args.getByName` getter +- Remove unused `Effects` class +- Improvements for `TransformKind` transformation parsing + +### Fixed + +- Issue with `ed25519` private key length + +## [5.0.0-rc7] - 2024-12-13 + +### Added + +- Checksummed `PublicKey` hex +- Improvements in `Transaction` creation from JSON +- Ability to send `Deploy` with the `RPC.putTransaction` method +- Renamed `StoredValue.prepaid` to `StoredValue.prepayment` +- Improvements in RPC client error processing + +### Fixed + +- Issue with implicit `axios` dependency +- Issue with `secp256k1` bytes +- Issue with `StepPayload.executionEffects` annotations +- Issue with `ExecutionResult` parsing from JSON + ## [5.0.0-rc6] - 2024-12-08 ### Added diff --git a/package-lock.json b/package-lock.json index 3dd5cbee..1e982210 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "casper-js-sdk", - "version": "5.0.0-rc7", + "version": "5.0.0-rc8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "casper-js-sdk", - "version": "5.0.0-rc7", + "version": "5.0.0-rc8", "license": "Apache 2.0", "dependencies": { "@ethersproject/bignumber": "^5.0.8", diff --git a/package.json b/package.json index 83d1f12c..84a27083 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "casper-js-sdk", - "version": "5.0.0-rc7", + "version": "5.0.0-rc8", "license": "Apache 2.0", "description": "SDK to interact with the Casper blockchain", "homepage": "https://github.com/casper-ecosystem/casper-js-sdk#README.md",