diff --git a/sdk/typescript/README.md b/sdk/typescript/README.md index 3cc1054535bde..b72094acd98c6 100644 --- a/sdk/typescript/README.md +++ b/sdk/typescript/README.md @@ -151,3 +151,24 @@ const moveCallTxn = await signer.executeMoveCall({ }); console.log('moveCallTxn', moveCallTxn); ``` + +To publish a package: + +```typescript +import { Ed25519Keypair, JsonRpcProvider, RawSigner } from '@mysten/sui.js'; +import * as fs from 'fs/promises'; +// Generate a new Keypair +const keypair = new Ed25519Keypair(); +const signer = new RawSigner( + keypair, + new JsonRpcProvider('https://gateway.devnet.sui.io:443') +); +const bytecode = await fs.readFile('path/to/project/build/project_name/bytecode_modules/module_name.mv', 'base64'); +const publishTxn = await signer.publish( + { + compiledModules: [bytecode.toString()], + gasBudget: 1000 + } +); +console.log('publishTxn', publishTxn); +``` diff --git a/sdk/typescript/src/index.guard.ts b/sdk/typescript/src/index.guard.ts index d75be21950266..cf5e30425a958 100644 --- a/sdk/typescript/src/index.guard.ts +++ b/sdk/typescript/src/index.guard.ts @@ -5,7 +5,7 @@ * Generated type guards for "index.ts". * WARNING: Do not manually change this file. */ -import { Ed25519KeypairData, Keypair, PublicKeyInitData, PublicKeyData, TransferObjectTransaction, MergeCoinTransaction, SplitCoinTransaction, MoveCallTransaction, TxnDataSerializer, SignaturePubkeyPair, Signer, TransactionDigest, SuiAddress, ObjectOwner, SuiObjectRef, SuiObjectInfo, ObjectContentFields, MovePackageContent, SuiData, SuiMoveObject, SuiMovePackage, SuiObject, ObjectStatus, ObjectType, GetOwnedObjectsResponse, GetObjectDataResponse, ObjectDigest, ObjectId, SequenceNumber, TransferObject, TransactionKindName, SuiTransactionKind, TransactionData, EpochId, AuthorityQuorumSignInfo, CertifiedTransaction, GasCostSummary, ExecutionStatusType, ExecutionStatus, OwnedObjectRef, TransactionEffects, TransactionEffectsResponse, GatewayTxSeqNumber, GetTxnDigestsResponse, MoveCall, SuiJsonValue, EmptySignInfo, AuthorityName, AuthoritySignature, TransactionBytes, MergeCoinResponse, SplitCoinResponse, TransactionResponse } from "./index"; +import { Ed25519KeypairData, Keypair, PublicKeyInitData, PublicKeyData, TransferObjectTransaction, MergeCoinTransaction, SplitCoinTransaction, MoveCallTransaction, PublishTransaction, TxnDataSerializer, SignaturePubkeyPair, Signer, TransactionDigest, SuiAddress, ObjectOwner, SuiObjectRef, SuiObjectInfo, ObjectContentFields, MovePackageContent, SuiData, SuiMoveObject, SuiMovePackage, SuiObject, ObjectStatus, ObjectType, GetOwnedObjectsResponse, GetObjectDataResponse, ObjectDigest, ObjectId, SequenceNumber, TransferObject, TransactionKindName, SuiTransactionKind, TransactionData, EpochId, AuthorityQuorumSignInfo, CertifiedTransaction, GasCostSummary, ExecutionStatusType, ExecutionStatus, OwnedObjectRef, TransactionEffects, TransactionEffectsResponse, GatewayTxSeqNumber, GetTxnDigestsResponse, MoveCall, SuiJsonValue, EmptySignInfo, AuthorityName, AuthoritySignature, TransactionBytes, MergeCoinResponse, SplitCoinResponse, PublishResponse, SuiPackage, TransactionResponse } from "./index"; import { BN } from "bn.js"; import { Base64DataBuffer } from "./serialization/base64"; import { PublicKey } from "./cryptography/publickey"; @@ -117,6 +117,21 @@ export function isMoveCallTransaction(obj: any, _argumentName?: string): obj is ) } +export function isPublishTransaction(obj: any, _argumentName?: string): obj is PublishTransaction { + return ( + (obj !== null && + typeof obj === "object" || + typeof obj === "function") && + Array.isArray(obj.compiledModules) && + obj.compiledModules.every((e: any) => + isTransactionDigest(e) as boolean + ) && + (typeof obj.gasPayment === "undefined" || + isTransactionDigest(obj.gasPayment) as boolean) && + isSequenceNumber(obj.gasBudget) as boolean + ) +} + export function isTxnDataSerializer(obj: any, _argumentName?: string): obj is TxnDataSerializer { return ( (obj !== null && @@ -125,7 +140,8 @@ export function isTxnDataSerializer(obj: any, _argumentName?: string): obj is Tx typeof obj.newTransferObject === "function" && typeof obj.newMoveCall === "function" && typeof obj.newMergeCoin === "function" && - typeof obj.newSplitCoin === "function" + typeof obj.newSplitCoin === "function" && + typeof obj.newPublish === "function" ) } @@ -615,6 +631,32 @@ export function isSplitCoinResponse(obj: any, _argumentName?: string): obj is Sp ) } +export function isPublishResponse(obj: any, _argumentName?: string): obj is PublishResponse { + return ( + (obj !== null && + typeof obj === "object" || + typeof obj === "function") && + isCertifiedTransaction(obj.certificate) as boolean && + Array.isArray(obj.createdObjects) && + obj.createdObjects.every((e: any) => + isSuiObject(e) as boolean + ) && + isSuiPackage(obj.package) as boolean && + isSuiObject(obj.updatedGas) as boolean + ) +} + +export function isSuiPackage(obj: any, _argumentName?: string): obj is SuiPackage { + return ( + (obj !== null && + typeof obj === "object" || + typeof obj === "function") && + isTransactionDigest(obj.digest) as boolean && + isTransactionDigest(obj.objectId) as boolean && + isSequenceNumber(obj.version) as boolean + ) +} + export function isTransactionResponse(obj: any, _argumentName?: string): obj is TransactionResponse { return ( ((obj !== null && @@ -628,6 +670,10 @@ export function isTransactionResponse(obj: any, _argumentName?: string): obj is (obj !== null && typeof obj === "object" || typeof obj === "function") && - isMergeCoinResponse(obj.MergeCoinResponse) as boolean) + isMergeCoinResponse(obj.MergeCoinResponse) as boolean || + (obj !== null && + typeof obj === "object" || + typeof obj === "function") && + isPublishResponse(obj.PublishResponse) as boolean) ) } diff --git a/sdk/typescript/src/signers/signer-with-provider.ts b/sdk/typescript/src/signers/signer-with-provider.ts index 3b07f8f49add4..5e59d37b0dfd3 100644 --- a/sdk/typescript/src/signers/signer-with-provider.ts +++ b/sdk/typescript/src/signers/signer-with-provider.ts @@ -14,6 +14,7 @@ import { SplitCoinTransaction, TransferObjectTransaction, TxnDataSerializer, + PublishTransaction, } from './txn-data-serializers/txn-data-serializer'; /////////////////////////////// @@ -120,4 +121,15 @@ export abstract class SignerWithProvider implements Signer { ); return await this.signAndExecuteTransaction(txBytes); } + + async publish( + transaction: PublishTransaction + ): Promise { + const signerAddress = await this.getAddress(); + const txBytes = await this.serializer.newPublish( + signerAddress, + transaction + ); + return await this.signAndExecuteTransaction(txBytes); + } } diff --git a/sdk/typescript/src/signers/txn-data-serializers/rpc-txn-data-serializer.ts b/sdk/typescript/src/signers/txn-data-serializers/rpc-txn-data-serializer.ts index bde70fe86d6c8..9e159efa8b665 100644 --- a/sdk/typescript/src/signers/txn-data-serializers/rpc-txn-data-serializer.ts +++ b/sdk/typescript/src/signers/txn-data-serializers/rpc-txn-data-serializer.ts @@ -10,6 +10,7 @@ import { MergeCoinTransaction, SplitCoinTransaction, TransferObjectTransaction, + PublishTransaction, TxnDataSerializer, } from './txn-data-serializer'; @@ -116,4 +117,25 @@ export class RpcTxnDataSerializer implements TxnDataSerializer { throw new Error(`Error splitting coin: ${err}`); } } + + async newPublish( + signerAddress: SuiAddress, + t: PublishTransaction + ): Promise { + try { + const resp = await this.client.requestWithType( + 'sui_publish', + [ + signerAddress, + t.compiledModules, + t.gasPayment, + t.gasBudget, + ], + isTransactionBytes + ); + return new Base64DataBuffer(resp.txBytes); + } catch (err) { + throw new Error(`Error publishing package ${err}`); + } + } } diff --git a/sdk/typescript/src/signers/txn-data-serializers/txn-data-serializer.ts b/sdk/typescript/src/signers/txn-data-serializers/txn-data-serializer.ts index 5b536810130ee..e5965b57f4847 100644 --- a/sdk/typescript/src/signers/txn-data-serializers/txn-data-serializer.ts +++ b/sdk/typescript/src/signers/txn-data-serializers/txn-data-serializer.ts @@ -37,6 +37,13 @@ export interface MoveCallTransaction { gasBudget: number; } +export interface PublishTransaction { + compiledModules: string[], + gasPayment?: ObjectId; + gasBudget: number; +} + + /////////////////////////////// // Exported Abstracts /** @@ -62,4 +69,9 @@ export interface TxnDataSerializer { signerAddress: SuiAddress, txn: SplitCoinTransaction ): Promise; + + newPublish( + signerAddress: SuiAddress, + txn: PublishTransaction + ): Promise; } diff --git a/sdk/typescript/src/types/transactions.ts b/sdk/typescript/src/types/transactions.ts index fb21cc00a9c99..78ee27664b3aa 100644 --- a/sdk/typescript/src/types/transactions.ts +++ b/sdk/typescript/src/types/transactions.ts @@ -135,16 +135,31 @@ export type SplitCoinResponse = { updatedGas: SuiObject; }; +export type PublishResponse = { + certificate: CertifiedTransaction; + createdObjects: SuiObject[]; + package: SuiPackage; + updatedGas: SuiObject; +} + +export type SuiPackage = { + digest: string; + objectId: string; + version: number; +} + export type TransactionResponse = | { EffectResponse: TransactionEffectsResponse; - // TODO: Add Publish Response } | { SplitCoinResponse: SplitCoinResponse; } | { MergeCoinResponse: MergeCoinResponse; + } + | { + PublishResponse: PublishResponse; }; /* -------------------------------------------------------------------------- */ @@ -273,6 +288,12 @@ export function getMergeCoinResponse( return 'MergeCoinResponse' in data ? data.MergeCoinResponse : undefined; } +export function getPublishResponse( + data: TransactionResponse +): PublishResponse | undefined { + return 'PublishResponse' in data ? data.PublishResponse : undefined; +} + /** * Get the updated coin after a merge. * @param data the response for executing a merge coin transaction