diff --git a/.changeset/seven-falcons-return.md b/.changeset/seven-falcons-return.md new file mode 100644 index 00000000000..10df0e1b94d --- /dev/null +++ b/.changeset/seven-falcons-return.md @@ -0,0 +1,12 @@ +--- +'@iota/bcs': minor +'@iota/create-dapp': minor +'@iota/dapp-kit': minor +'@iota/graphql-transport': minor +'@iota/kiosk': minor +'@iota/ledgerjs-hw-app-iota': minor +'@iota/iota-sdk': minor +'@iota/wallet-standard': minor +--- + +Changes for compatibility with the node, simplification of exposed APIs and general improvements. diff --git a/apps/explorer/tests/utils/localnet.ts b/apps/explorer/tests/utils/localnet.ts index a87ab04e61f..a03f865afa3 100644 --- a/apps/explorer/tests/utils/localnet.ts +++ b/apps/explorer/tests/utils/localnet.ts @@ -36,7 +36,6 @@ export async function split_coin(address: string) { showEffects: true, showEvents: true, }, - requestType: 'WaitForLocalExecution', }); return result; diff --git a/apps/wallet/src/background/Transactions.ts b/apps/wallet/src/background/Transactions.ts index 95590bf8c0f..c7182567c1e 100644 --- a/apps/wallet/src/background/Transactions.ts +++ b/apps/wallet/src/background/Transactions.ts @@ -10,9 +10,8 @@ import type { IotaSignTransactionSerialized } from '_payloads/transactions/Execu import { type SignMessageRequest } from '_payloads/transactions/SignMessage'; import type { TransactionRequestResponse } from '_payloads/transactions/ui/TransactionRequestResponse'; import type { ContentScriptConnection } from '_src/background/connections/ContentScriptConnection'; -import { type SignedTransaction } from '_src/ui/app/WalletSigner'; import { type IotaTransactionBlockResponse } from '@iota/iota-sdk/client'; -import { type IotaSignMessageOutput } from '@iota/wallet-standard'; +import { type SignedTransaction, type IotaSignPersonalMessageOutput } from '@iota/wallet-standard'; import { filter, lastValueFrom, map, race, Subject, take } from 'rxjs'; import { v4 as uuidV4 } from 'uuid'; import Browser from 'webextension-polyfill'; @@ -68,12 +67,12 @@ class Transactions { return txSigned!; } - public async signMessage( + public async signPersonalMessage( { accountAddress, message }: Required>['args'], connection: ContentScriptConnection, - ): Promise { + ): Promise { const { txResult, txResultError } = await this.requestApproval( - { type: 'sign-message', accountAddress, message }, + { type: 'sign-personal-message', accountAddress, message }, connection.origin, connection.originFavIcon, ); @@ -86,7 +85,7 @@ class Transactions { if (!('messageBytes' in txResult)) { throw new Error('Sign message error, unknown result'); } - return txResult; + return txResult as IotaSignPersonalMessageOutput; } public async getTransactionRequests(): Promise> { diff --git a/apps/wallet/src/background/connections/ContentScriptConnection.ts b/apps/wallet/src/background/connections/ContentScriptConnection.ts index 2cb2f454717..8067c3ee7e5 100644 --- a/apps/wallet/src/background/connections/ContentScriptConnection.ts +++ b/apps/wallet/src/background/connections/ContentScriptConnection.ts @@ -29,9 +29,9 @@ import { isSignMessageRequest, type SignMessageRequest, } from '_src/shared/messaging/messages/payloads/transactions/SignMessage'; -import { type SignedTransaction } from '_src/ui/app/WalletSigner'; import { type IotaTransactionBlockResponse } from '@iota/iota-sdk/client'; import type { Runtime } from 'webextension-polyfill'; +import { type SignedTransaction } from '@iota/wallet-standard'; import { getAccountsStatusData } from '../accounts'; import NetworkEnv from '../NetworkEnv'; @@ -137,10 +137,10 @@ export class ContentScriptConnection extends Connection { ['viewAccount', 'suggestTransactions'], payload.args.accountAddress, ); - const result = await Transactions.signMessage(payload.args, this); + const result = await Transactions.signPersonalMessage(payload.args, this); this.send( createMessage( - { type: 'sign-message-request', return: result }, + { type: 'sign-personal-message-request', return: result }, msg.id, ), ); diff --git a/apps/wallet/src/dapp-interface/WalletStandardInterface.ts b/apps/wallet/src/dapp-interface/WalletStandardInterface.ts index 1902099b3f0..29429b238dd 100644 --- a/apps/wallet/src/dapp-interface/WalletStandardInterface.ts +++ b/apps/wallet/src/dapp-interface/WalletStandardInterface.ts @@ -25,7 +25,6 @@ import { getCustomNetwork, type NetworkEnvType } from '_src/shared/api-env'; import { type SignMessageRequest } from '_src/shared/messaging/messages/payloads/transactions/SignMessage'; import { isWalletStatusChangePayload } from '_src/shared/messaging/messages/payloads/wallet-status-change'; import { getNetwork, Network, type ChainType } from '@iota/iota-sdk/client'; -import { isTransaction } from '@iota/iota-sdk/transactions'; import { fromB64, toB64 } from '@iota/iota-sdk/utils'; import { ReadonlyWalletAccount, @@ -36,10 +35,7 @@ import { type StandardEventsListeners, type StandardEventsOnMethod, type IotaFeatures, - type IotaSignAndExecuteTransactionBlockMethod, - type IotaSignMessageMethod, type IotaSignPersonalMessageMethod, - type IotaSignTransactionBlockMethod, type Wallet, type IotaSignTransactionMethod, type IotaSignAndExecuteTransactionMethod, @@ -94,26 +90,14 @@ export class IotaWallet implements Wallet { version: '1.0.0', on: this.#on, }, - 'iota:signTransactionBlock': { - version: '1.0.0', - signTransactionBlock: this.#signTransactionBlock, - }, 'iota:signTransaction': { version: '2.0.0', signTransaction: this.#signTransaction, }, - 'iota:signAndExecuteTransactionBlock': { - version: '1.0.0', - signAndExecuteTransactionBlock: this.#signAndExecuteTransactionBlock, - }, 'iota:signAndExecuteTransaction': { version: '2.0.0', signAndExecuteTransaction: this.#signAndExecuteTransaction, }, - 'iota:signMessage': { - version: '1.0.0', - signMessage: this.#signMessage, - }, 'iota:signPersonalMessage': { version: '1.0.0', signPersonalMessage: this.#signPersonalMessage, @@ -205,32 +189,6 @@ export class IotaWallet implements Wallet { return { accounts: this.accounts }; }; - #signTransactionBlock: IotaSignTransactionBlockMethod = async ({ - transactionBlock, - account, - ...input - }) => { - if (!isTransaction(transactionBlock)) { - throw new Error( - 'Unexpected transaction format found. Ensure that you are using the `Transaction` class.', - ); - } - - return mapToPromise( - this.#send({ - type: 'sign-transaction-request', - transaction: { - ...input, - // account might be undefined if previous version of adapters is used - // in that case use the first account address - account: account?.address || this.#accounts[0]?.address || '', - transaction: transactionBlock.serialize(), - }, - }), - (response) => response.result, - ); - }; - #signTransaction: IotaSignTransactionMethod = async ({ transaction, account, ...input }) => { return mapToPromise( this.#send({ @@ -243,36 +201,13 @@ export class IotaWallet implements Wallet { transaction: await transaction.toJSON(), }, }), - ({ result: { signature, transactionBlockBytes: bytes } }) => ({ + ({ result: { signature, bytes } }) => ({ signature, bytes, }), ); }; - #signAndExecuteTransactionBlock: IotaSignAndExecuteTransactionBlockMethod = async (input) => { - if (!isTransaction(input.transactionBlock)) { - throw new Error( - 'Unexpected transaction format found. Ensure that you are using the `Transaction` class.', - ); - } - - return mapToPromise( - this.#send({ - type: 'execute-transaction-request', - transaction: { - type: 'transaction', - data: input.transactionBlock.serialize(), - options: input.options, - // account might be undefined if previous version of adapters is used - // in that case use the first account address - account: input.account?.address || this.#accounts[0]?.address || '', - }, - }), - (response) => response.result, - ); - }; - #signAndExecuteTransaction: IotaSignAndExecuteTransactionMethod = async (input) => { return mapToPromise( this.#send({ @@ -309,28 +244,10 @@ export class IotaWallet implements Wallet { ); }; - #signMessage: IotaSignMessageMethod = async ({ message, account }) => { - return mapToPromise( - this.#send({ - type: 'sign-message-request', - args: { - message: toB64(message), - accountAddress: account.address, - }, - }), - (response) => { - if (!response.return) { - throw new Error('Invalid sign message response'); - } - return response.return; - }, - ); - }; - #signPersonalMessage: IotaSignPersonalMessageMethod = async ({ message, account }) => { return mapToPromise( this.#send({ - type: 'sign-message-request', + type: 'sign-personal-message-request', args: { message: toB64(message), accountAddress: account.address, @@ -340,10 +257,7 @@ export class IotaWallet implements Wallet { if (!response.return) { throw new Error('Invalid sign message response'); } - return { - bytes: response.return.messageBytes, - signature: response.return.signature, - }; + return response.return; }, ); }; diff --git a/apps/wallet/src/shared/messaging/messages/payloads/BasePayload.ts b/apps/wallet/src/shared/messaging/messages/payloads/BasePayload.ts index 904cdb290aa..4de18c06bdc 100644 --- a/apps/wallet/src/shared/messaging/messages/payloads/BasePayload.ts +++ b/apps/wallet/src/shared/messaging/messages/payloads/BasePayload.ts @@ -30,7 +30,7 @@ export type PayloadType = | 'features-response' | 'get-network' | 'set-network' - | 'sign-message-request' + | 'sign-personal-message-request' | 'method-payload' | 'derive-bip-path-accounts-finder' | 'derive-bip-path-accounts-finder-response' diff --git a/apps/wallet/src/shared/messaging/messages/payloads/transactions/ApprovalRequest.ts b/apps/wallet/src/shared/messaging/messages/payloads/transactions/ApprovalRequest.ts index 49782caec47..80685dd2aa6 100644 --- a/apps/wallet/src/shared/messaging/messages/payloads/transactions/ApprovalRequest.ts +++ b/apps/wallet/src/shared/messaging/messages/payloads/transactions/ApprovalRequest.ts @@ -5,8 +5,8 @@ import { type SignedTransaction } from '_src/ui/app/WalletSigner'; import type { IotaTransactionBlockResponse } from '@iota/iota-sdk/client'; import { - type IotaSignAndExecuteTransactionBlockInput, - type IotaSignMessageOutput, + type IotaSignAndExecuteTransactionInput, + type IotaSignPersonalMessageOutput, } from '@iota/wallet-standard'; export type TransactionDataType = { @@ -14,12 +14,11 @@ export type TransactionDataType = { data: string; account: string; justSign?: boolean; - requestType?: IotaSignAndExecuteTransactionBlockInput['requestType']; - options?: IotaSignAndExecuteTransactionBlockInput['options']; + options?: IotaSignAndExecuteTransactionInput['options']; }; export type SignMessageDataType = { - type: 'sign-message'; + type: 'sign-personal-message'; message: string; accountAddress: string; }; @@ -29,16 +28,17 @@ export type ApprovalRequest = { approved: boolean | null; origin: string; originFavIcon?: string; - txResult?: IotaTransactionBlockResponse | IotaSignMessageOutput; + txResult?: IotaTransactionBlockResponse | IotaSignPersonalMessageOutput; txResultError?: string; txSigned?: SignedTransaction; createdDate: string; tx: TransactionDataType | SignMessageDataType; }; -export interface SignMessageApprovalRequest extends Omit { +export interface SignPersonalMessageApprovalRequest + extends Omit { tx: SignMessageDataType; - txResult?: IotaSignMessageOutput; + txResult?: IotaSignPersonalMessageOutput; } export interface TransactionApprovalRequest extends Omit { @@ -46,14 +46,14 @@ export interface TransactionApprovalRequest extends Omit & { transaction: string; account: string; diff --git a/apps/wallet/src/shared/messaging/messages/payloads/transactions/ExecuteTransactionResponse.ts b/apps/wallet/src/shared/messaging/messages/payloads/transactions/ExecuteTransactionResponse.ts index b685c9007f5..02953d0f89c 100644 --- a/apps/wallet/src/shared/messaging/messages/payloads/transactions/ExecuteTransactionResponse.ts +++ b/apps/wallet/src/shared/messaging/messages/payloads/transactions/ExecuteTransactionResponse.ts @@ -5,7 +5,7 @@ import { isBasePayload } from '_payloads'; import type { BasePayload, Payload } from '_payloads'; import type { IotaTransactionBlockResponse } from '@iota/iota-sdk/client'; -import { type IotaSignTransactionBlockOutput } from '@iota/wallet-standard'; +import { type IotaSignTransactionOutput } from '@iota/wallet-standard'; export interface ExecuteTransactionResponse extends BasePayload { type: 'execute-transaction-response'; @@ -20,7 +20,7 @@ export function isExecuteTransactionResponse( export interface SignTransactionResponse extends BasePayload { type: 'sign-transaction-response'; - result: IotaSignTransactionBlockOutput; + result: IotaSignTransactionOutput; } export function isSignTransactionResponse(payload: Payload): payload is SignTransactionResponse { diff --git a/apps/wallet/src/shared/messaging/messages/payloads/transactions/SignMessage.ts b/apps/wallet/src/shared/messaging/messages/payloads/transactions/SignMessage.ts index 6e88039c7f3..4dd520639b1 100644 --- a/apps/wallet/src/shared/messaging/messages/payloads/transactions/SignMessage.ts +++ b/apps/wallet/src/shared/messaging/messages/payloads/transactions/SignMessage.ts @@ -2,20 +2,20 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { type IotaSignMessageOutput } from '@iota/wallet-standard'; +import { type IotaSignPersonalMessageOutput } from '@iota/wallet-standard'; import { isBasePayload, type BasePayload } from '../BasePayload'; import { type Payload } from '../Payload'; export interface SignMessageRequest extends BasePayload { - type: 'sign-message-request'; + type: 'sign-personal-message-request'; args?: { message: string; // base64 accountAddress: string; }; - return?: IotaSignMessageOutput; + return?: IotaSignPersonalMessageOutput; } export function isSignMessageRequest(payload: Payload): payload is SignMessageRequest { - return isBasePayload(payload) && payload.type === 'sign-message-request'; + return isBasePayload(payload) && payload.type === 'sign-personal-message-request'; } diff --git a/apps/wallet/src/shared/messaging/messages/payloads/transactions/ui/TransactionRequestResponse.ts b/apps/wallet/src/shared/messaging/messages/payloads/transactions/ui/TransactionRequestResponse.ts index 8b04454b22e..ccfc53a55c3 100644 --- a/apps/wallet/src/shared/messaging/messages/payloads/transactions/ui/TransactionRequestResponse.ts +++ b/apps/wallet/src/shared/messaging/messages/payloads/transactions/ui/TransactionRequestResponse.ts @@ -6,13 +6,13 @@ import { isBasePayload } from '_payloads'; import type { BasePayload, Payload } from '_payloads'; import { type SignedTransaction } from '_src/ui/app/WalletSigner'; import type { IotaTransactionBlockResponse } from '@iota/iota-sdk/client'; -import { type IotaSignMessageOutput } from '@iota/wallet-standard'; +import { type IotaSignPersonalMessageOutput } from '@iota/wallet-standard'; export interface TransactionRequestResponse extends BasePayload { type: 'transaction-request-response'; txID: string; approved: boolean; - txResult?: IotaTransactionBlockResponse | IotaSignMessageOutput; + txResult?: IotaTransactionBlockResponse | IotaSignPersonalMessageOutput; txResultError?: string; txSigned?: SignedTransaction; } diff --git a/apps/wallet/src/ui/app/WalletSigner.ts b/apps/wallet/src/ui/app/WalletSigner.ts index fdf8d9ee07a..2be83cb253e 100644 --- a/apps/wallet/src/ui/app/WalletSigner.ts +++ b/apps/wallet/src/ui/app/WalletSigner.ts @@ -5,7 +5,6 @@ import { bcs } from '@iota/iota-sdk/bcs'; import { type DryRunTransactionBlockResponse, - type ExecuteTransactionRequestType, type IotaClient, type IotaTransactionBlockResponse, type IotaTransactionBlockResponseOptions, @@ -14,13 +13,13 @@ import { messageWithIntent } from '@iota/iota-sdk/cryptography'; import { isTransaction, type Transaction } from '@iota/iota-sdk/transactions'; import { fromB64, toB64 } from '@iota/iota-sdk/utils'; -export type SignedTransaction = { - transactionBlockBytes: string; +export interface SignedTransaction { + bytes: string; signature: string; -}; +} export type SignedMessage = { - messageBytes: string; + bytes: string; signature: string; }; @@ -31,14 +30,11 @@ export abstract class WalletSigner { this.client = client; } - abstract signData(data: Uint8Array, clientIdentifier?: string): Promise; + abstract signData(data: Uint8Array): Promise; abstract getAddress(): Promise; - async signMessage( - input: { message: Uint8Array }, - clientIdentifier?: string, - ): Promise { + async signMessage(input: { message: Uint8Array }): Promise { const signature = await this.signData( messageWithIntent( 'PersonalMessage', @@ -47,64 +43,56 @@ export abstract class WalletSigner { ); return { - messageBytes: toB64(input.message), + bytes: toB64(input.message), signature, }; } - protected async prepareTransactionBlock(transactionBlock: Uint8Array | Transaction | string) { - if (isTransaction(transactionBlock)) { + protected async prepareTransaction(transaction: Uint8Array | Transaction | string) { + if (isTransaction(transaction)) { // If the sender has not yet been set on the transaction, then set it. // NOTE: This allows for signing transactions with mismatched senders, which is important for sponsored transactions. - transactionBlock.setSenderIfNotSet(await this.getAddress()); - return await transactionBlock.build({ + transaction.setSenderIfNotSet(await this.getAddress()); + return await transaction.build({ client: this.client, }); } - if (typeof transactionBlock === 'string') { - return fromB64(transactionBlock); + if (typeof transaction === 'string') { + return fromB64(transaction); } - if (transactionBlock instanceof Uint8Array) { - return transactionBlock; + if (transaction instanceof Uint8Array) { + return transaction; } throw new Error('Unknown transaction format'); } - async signTransactionBlock( - input: { - transactionBlock: Uint8Array | Transaction; - }, - clientIdentifier?: string, - ): Promise { - const bytes = await this.prepareTransactionBlock(input.transactionBlock); + async signTransaction(input: { + transaction: Uint8Array | Transaction; + }): Promise { + const bytes = await this.prepareTransaction(input.transaction); const signature = await this.signData(messageWithIntent('TransactionData', bytes)); return { - transactionBlockBytes: toB64(bytes), + bytes: toB64(bytes), signature, }; } - async signAndExecuteTransaction( - input: { - transactionBlock: Uint8Array | Transaction; - options?: IotaTransactionBlockResponseOptions; - requestType?: ExecuteTransactionRequestType; - }, - clientIdentifier?: string, - ): Promise { - const bytes = await this.prepareTransactionBlock(input.transactionBlock); - const signed = await this.signTransactionBlock({ - transactionBlock: bytes, + async signAndExecuteTransaction(input: { + transactionBlock: Uint8Array | Transaction; + options?: IotaTransactionBlockResponseOptions; + }): Promise { + const bytes = await this.prepareTransaction(input.transactionBlock); + const signed = await this.signTransaction({ + transaction: bytes, }); return this.client.executeTransactionBlock({ transactionBlock: bytes, signature: signed.signature, options: input.options, - requestType: input.requestType, }); } @@ -112,7 +100,7 @@ export abstract class WalletSigner { transactionBlock: Transaction | string | Uint8Array; }): Promise { return this.client.dryRunTransactionBlock({ - transactionBlock: await this.prepareTransactionBlock(input.transactionBlock), + transactionBlock: await this.prepareTransaction(input.transactionBlock), }); } } diff --git a/apps/wallet/src/ui/app/pages/approval-request/SignMessageRequest.tsx b/apps/wallet/src/ui/app/pages/approval-request/SignMessageRequest.tsx index 72b880f9e51..b30ac753ffc 100644 --- a/apps/wallet/src/ui/app/pages/approval-request/SignMessageRequest.tsx +++ b/apps/wallet/src/ui/app/pages/approval-request/SignMessageRequest.tsx @@ -2,7 +2,7 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { type SignMessageApprovalRequest } from '_payloads/transactions/ApprovalRequest'; +import { type SignPersonalMessageApprovalRequest } from '_payloads/transactions/ApprovalRequest'; import { toUtf8OrB64 } from '_src/shared/utils'; import { useMemo } from 'react'; import { UserApproveContainer } from '_components'; @@ -14,7 +14,7 @@ import { PageMainLayoutTitle } from '../../shared/page-main-layout/PageMainLayou import { Panel } from '@iota/apps-ui-kit'; export interface SignMessageRequestProps { - request: SignMessageApprovalRequest; + request: SignPersonalMessageApprovalRequest; } export function SignMessageRequest({ request }: SignMessageRequestProps) { diff --git a/apps/wallet/src/ui/app/pages/approval-request/index.tsx b/apps/wallet/src/ui/app/pages/approval-request/index.tsx index 7528680d6ff..14c7dafa767 100644 --- a/apps/wallet/src/ui/app/pages/approval-request/index.tsx +++ b/apps/wallet/src/ui/app/pages/approval-request/index.tsx @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 import { - isSignMessageApprovalRequest, + isSignPersonalMessageApprovalRequest, isTransactionApprovalRequest, } from '_payloads/transactions/ApprovalRequest'; import { useEffect, useMemo } from 'react'; @@ -35,7 +35,7 @@ export function ApprovalRequestPage() { return ( {request ? ( - isSignMessageApprovalRequest(request) ? ( + isSignPersonalMessageApprovalRequest(request) ? ( ) : isTransactionApprovalRequest(request) ? ( diff --git a/apps/wallet/src/ui/app/pages/home/nft-transfer/useTransferKioskItem.tsx b/apps/wallet/src/ui/app/pages/home/nft-transfer/useTransferKioskItem.tsx index e853d772f25..f4edd7930bc 100644 --- a/apps/wallet/src/ui/app/pages/home/nft-transfer/useTransferKioskItem.tsx +++ b/apps/wallet/src/ui/app/pages/home/nft-transfer/useTransferKioskItem.tsx @@ -37,7 +37,7 @@ export function useTransferKioskItem({ const kioskClient = useKioskClient(); return useMutation({ - mutationFn: async ({ to, clientIdentifier }: { to: string; clientIdentifier?: string }) => { + mutationFn: async ({ to }: { to: string }) => { if (!to || !signer || !objectType) { throw new Error('Missing data'); } @@ -60,17 +60,14 @@ export function useTransferKioskItem({ }) .finalize(); - return signer.signAndExecuteTransaction( - { - transactionBlock: txb, - options: { - showInput: true, - showEffects: true, - showEvents: true, - }, + return signer.signAndExecuteTransaction({ + transactionBlock: txb, + options: { + showInput: true, + showEffects: true, + showEvents: true, }, - clientIdentifier, - ); + }); } if (kiosk.type === KioskTypes.ORIGINBYTE && objectData?.data?.data?.type) { @@ -102,17 +99,14 @@ export function useTransferKioskItem({ arguments: [tx.object(kioskId), tx.pure.address(to), tx.pure.id(objectId)], }); } - return signer.signAndExecuteTransaction( - { - transactionBlock: tx, - options: { - showInput: true, - showEffects: true, - showEvents: true, - }, + return signer.signAndExecuteTransaction({ + transactionBlock: tx, + options: { + showInput: true, + showEffects: true, + showEvents: true, }, - clientIdentifier, - ); + }); } throw new Error('Failed to transfer object'); }, diff --git a/apps/wallet/src/ui/app/redux/slices/transaction-requests/index.ts b/apps/wallet/src/ui/app/redux/slices/transaction-requests/index.ts index 1c06ca460e5..3b5e0ee7fdc 100644 --- a/apps/wallet/src/ui/app/redux/slices/transaction-requests/index.ts +++ b/apps/wallet/src/ui/app/redux/slices/transaction-requests/index.ts @@ -35,15 +35,11 @@ export const respondToTransactionRequest = createAsyncThunk< txRequestID: string; approved: boolean; signer: WalletSigner; - clientIdentifier?: string; }, AppThunkConfig >( 'respond-to-transaction-request', - async ( - { txRequestID, approved, signer, clientIdentifier }, - { extra: { background }, getState }, - ) => { + async ({ txRequestID, approved, signer }, { extra: { background }, getState }) => { const state = getState(); const txRequest = txRequestsSelectors.selectById(state, txRequestID); if (!txRequest) { @@ -54,32 +50,22 @@ export const respondToTransactionRequest = createAsyncThunk< let txResultError: string | undefined; if (approved) { try { - if (txRequest.tx.type === 'sign-message') { - txResult = await signer.signMessage( - { - message: fromB64(txRequest.tx.message), - }, - clientIdentifier, - ); + if (txRequest.tx.type === 'sign-personal-message') { + txResult = await signer.signMessage({ + message: fromB64(txRequest.tx.message), + }); } else if (txRequest.tx.type === 'transaction') { const tx = Transaction.from(txRequest.tx.data); if (txRequest.tx.justSign) { // Just a signing request, do not submit - txSigned = await signer.signTransactionBlock( - { - transactionBlock: tx, - }, - clientIdentifier, - ); + txSigned = await signer.signTransaction({ + transaction: tx, + }); } else { - txResult = await signer.signAndExecuteTransaction( - { - transactionBlock: tx, - options: txRequest.tx.options, - requestType: txRequest.tx.requestType, - }, - clientIdentifier, - ); + txResult = await signer.signAndExecuteTransaction({ + transactionBlock: tx, + options: txRequest.tx.options, + }); } } else { throw new Error( diff --git a/apps/wallet/src/ui/app/staking/stake/StakingCard.tsx b/apps/wallet/src/ui/app/staking/stake/StakingCard.tsx index f451fdc3790..4e368f8c723 100644 --- a/apps/wallet/src/ui/app/staking/stake/StakingCard.tsx +++ b/apps/wallet/src/ui/app/staking/stake/StakingCard.tsx @@ -6,9 +6,7 @@ import { Loading } from '_components'; import { Coin } from '_redux/slices/iota-objects/Coin'; import { ampli } from '_src/shared/analytics/ampli'; import { MIN_NUMBER_IOTA_TO_STAKE } from '_src/shared/constants'; -import { useFeatureIsOn } from '@growthbook/growthbook-react'; import { - Feature, createStakeTransaction, createUnstakeTransaction, parseAmount, @@ -63,9 +61,6 @@ function StakingCard() { staleTime: DELEGATED_STAKES_QUERY_STALE_TIME, refetchInterval: DELEGATED_STAKES_QUERY_REFETCH_INTERVAL, }); - const effectsOnlySharedTransactions = useFeatureIsOn( - Feature.WalletEffectsOnlySharedTransaction as string, - ); const { data: system, isPending: validatorsIsPending } = useIotaClientQuery( 'getLatestIotaSystemState', @@ -126,17 +121,18 @@ function StakingCard() { // }); try { const transactionBlock = createStakeTransaction(amount, validatorAddress); - return await signer.signAndExecuteTransaction({ + const tx = await signer.signAndExecuteTransaction({ transactionBlock, - requestType: effectsOnlySharedTransactions - ? 'WaitForEffectsCert' - : 'WaitForLocalExecution', options: { showInput: true, showEffects: true, showEvents: true, }, }); + await signer.client.waitForTransaction({ + digest: tx.digest, + }); + return tx; } finally { // sentryTransaction.finish(); } @@ -159,17 +155,18 @@ function StakingCard() { // name: 'stake', // }); const transactionBlock = createUnstakeTransaction(stakedIotaId); - return await signer.signAndExecuteTransaction({ + const tx = await signer.signAndExecuteTransaction({ transactionBlock, - requestType: effectsOnlySharedTransactions - ? 'WaitForEffectsCert' - : 'WaitForLocalExecution', options: { showInput: true, showEffects: true, showEvents: true, }, }); + await signer.client.waitForTransaction({ + digest: tx.digest, + }); + return tx; // finally { // sentryTransaction.finish(); // } diff --git a/apps/wallet/tests/demo-app/src/index.tsx b/apps/wallet/tests/demo-app/src/index.tsx index 0f9f2cc3de1..8ee934abd37 100644 --- a/apps/wallet/tests/demo-app/src/index.tsx +++ b/apps/wallet/tests/demo-app/src/index.tsx @@ -131,7 +131,7 @@ function App() { onClick={async () => { setError(null); try { - await iotaWallet.features['iota:signMessage']?.signMessage({ + await iotaWallet.features['iota:signPersonalMessage']?.signPersonalMessage({ account: getAccount(accounts[0], useWrongAccounts), message: new TextEncoder().encode('Test message'), }); diff --git a/apps/wallet/tests/sites-to-cs-messaging.spec.ts b/apps/wallet/tests/sites-to-cs-messaging.spec.ts index ec33dcd6ac0..69ef27883a7 100644 --- a/apps/wallet/tests/sites-to-cs-messaging.spec.ts +++ b/apps/wallet/tests/sites-to-cs-messaging.spec.ts @@ -61,7 +61,7 @@ test.describe('site to content script messages', () => { [ 'sign message no account', { - type: 'sign-message-request', + type: 'sign-personal-message-request', }, false, ], diff --git a/crates/iota-open-rpc/spec/openrpc.json b/crates/iota-open-rpc/spec/openrpc.json index 009cc089d26..83f799bc5cb 100644 --- a/crates/iota-open-rpc/spec/openrpc.json +++ b/crates/iota-open-rpc/spec/openrpc.json @@ -883,7 +883,7 @@ }, { "name": "module", - "value": "iotafrens" + "value": "my_module" }, { "name": "function", diff --git a/crates/iota-open-rpc/src/examples.rs b/crates/iota-open-rpc/src/examples.rs index 74d6e570740..bf9c3b4eef9 100644 --- a/crates/iota-open-rpc/src/examples.rs +++ b/crates/iota-open-rpc/src/examples.rs @@ -938,7 +938,7 @@ impl RpcExampleProvider { "Returns the argument types for the package and function the request provides.", vec![ ("package", json!(ObjectID::new(self.rng.gen()))), - ("module", json!("iotafrens".to_string())), + ("module", json!("my_module".to_string())), ("function", json!("mint".to_string())), ], json!(result), diff --git a/docs/content/developer/iota-101/access-time.mdx b/docs/content/developer/iota-101/access-time.mdx index b218993255d..ac6358d4373 100644 --- a/docs/content/developer/iota-101/access-time.mdx +++ b/docs/content/developer/iota-101/access-time.mdx @@ -2,66 +2,85 @@ title: Access On-Chain Time description: Access network-based time for your transactions. IOTA provides a Clock module to capture near-real time or epoch time in your IOTA packages. --- + import Quiz from '@site/src/components/Quiz'; import questions from '/json/developer/iota-101/access-time.json'; -You have options when needing to access network-based time for your transactions. If you need a near real-time measurement (within a few seconds), use the immutable reference of time provided by the `Clock` module in Move. The reference value from this module updates with every network checkpoint. If you don't need as current a time slice, use the `epoch_timestamp_ms` function to capture the precise moment the current epoch started. +# Accessing On-Chain Time in IOTA + +When you need to access network-based time for your transactions on IOTA, you have several options: -## The iota::clock::Clock Module +- **Near Real-Time Measurement**: Use the immutable reference of time provided by the [`Clock`](../../references/framework/iota-framework/clock.mdx) module in Move. This value updates with every network checkpoint. +- **Epoch Start Time**: Use the [`epoch_timestamp_ms`](../../references/framework/iota-framework/tx_context.mdx#function-epoch_timestamp_ms) function to capture the precise moment the current epoch started. -To access a prompt timestamp, you must pass a read-only reference of `iota::clock::Clock` as an entry function parameter in your transactions. An instance of `Clock` is provided at address `0x6`, no new instances can be created. +## Using the `iota::clock::Clock` Module -Use the `timestamp_ms` function from the `iota::clock` module to extract a unix timestamp in milliseconds. +To access a prompt timestamp, you can pass a read-only reference of [`iota::clock::Clock`](../../references/framework/iota-framework/clock.mdx) as an entry function parameter in your transactions. +An instance of `Clock` is provided at the address `0x6`, and no new instances can be created. +Use the [`timestamp_ms`](../../references/framework/iota-framework/clock.mdx#function-timestamp_ms) function from the `iota::clock` module to extract a Unix timestamp in milliseconds. ```move file=/crates/iota-framework/packages/iota-framework/sources/clock.move#L29-L33 ``` -The example below demonstrates an entry function that emits an event containing a timestamp from the `Clock`: +### Example: Emitting an Event with a Timestamp + +The following example demonstrates an entry function that emits an event containing a timestamp from the `Clock`: ```move file=/examples/move/basics/sources/clock.move#L5-L15 ``` -A call to the previous entry function takes the following form, passing `0x6` as the address for the `Clock` parameter: +To call the previous entry function, pass `0x6` as the address for the `Clock` parameter: ```shell iota client call --package --module 'clock' --function 'access' --args '0x6' ``` -Expect the `Clock` timestamp to change at the rate the network generates checkpoints, which is **every 1 second** with Narwhal/Bullshark consensus and **every 0.1 to 0.2 seconds** with Mysticeti consensus. +**Note**: The `Clock` timestamp changes at the rate the network generates checkpoints, which is **every 1 second** with Narwhal/Bullshark consensus and **every 0.1 to 0.2 seconds** with Mysticeti consensus. -Successive calls to `iota::clock::timestamp_ms` in the same transaction always produce the same result (transactions are considered to take effect instantly), but timestamps from `Clock` are otherwise monotonic across transactions that touch the same shared objects: Successive transactions seeing a greater or equal timestamp to their predecessors. +Successive calls to `iota::clock::timestamp_ms` within the same transaction will always produce the same result because transactions are considered to take effect instantly. +However, timestamps from `Clock` are monotonic across transactions that touch the same shared objects, meaning successive transactions see a greater or equal timestamp compared to their predecessors. -Any transaction that requires access to a `Clock` must go through consensus because the only available instance is a shared object. As a result, this technique is not suitable for transactions that must use the single-owner fastpath (see Epoch timestamps for a single-owner-compatible source of timestamps). +### Transaction Requirements -Transactions that use the clock must accept it as an **immutable reference** (not a mutable reference or value). This prevents contention, as transactions that access the `Clock` can only read it, so do not need to be sequenced relative to each other. Validators refuse to sign transactions that do not meet this requirement and packages that include entry functions that accept a `Clock` or `&mut Clock` fail to publish. +- **Consensus Requirement**: Any transaction that requires access to a `Clock` must go through consensus because the only available instance is a shared object. +- **Immutable Reference**: Transactions that use the clock must accept it as an **immutable reference** (`&Clock`), not as a mutable reference or value. This prevents contention, as transactions that access the `Clock` can only read it. -The following functions test `Clock`-dependent code by manually creating a `Clock` object and manipulating its timestamp. This is possible only in test code: +Validators will refuse to sign transactions that do not meet this requirement, and packages that include entry functions accepting a `Clock` or `&mut Clock` will fail to publish. + +### Testing `Clock`-Dependent Code + +The following functions allow you to test `Clock`-dependent code by manually creating a `Clock` object and manipulating its timestamp. This is possible only in test code: ```move file=/crates/iota-framework/packages/iota-framework/sources/clock.move#L65-L93 ``` -The next example presents a basic test that creates a Clock, increments it, and then checks its value: - +Here's a basic test that creates a `Clock`, increments it, and then checks its value: ```move file=/crates/iota-framework/packages/iota-framework/tests/clock_tests.move#L6-L22 ``` -## Epoch Timestamps +## Using Epoch Timestamps -Use the following function from the `iota::tx_context` module to access the timestamp for the start of the current epoch for all transactions (including ones that do not go through consensus): +If you don't need a near real-time measurement, +you can use the [`epoch_timestamp_ms`](../../references/framework/iota-framework/tx_context.mdx#function-epoch_timestamp_ms) function +from the [`iota::tx_context`](../../references/framework/iota-framework/tx_context.mdx) module to access the timestamp for the start of the +current epoch. This function works for all transactions, including those that do not go through consensus: ```move file=/crates/iota-framework/packages/iota-framework/sources/tx_context.move#L54-L56 ``` -The preceding function returns the point in time when the current epoch started, as a millisecond granularity unix timestamp in a `u64`. This value changes roughly **once every 24 hours**, when the epoch changes. +The function returns the point in time when the current epoch started, as a Unix timestamp in milliseconds (`u64`). +This value changes roughly **once every 24 hours** when the epoch changes. -Tests based on `iota::test_scenario` can use `later_epoch` (following code), to exercise time-sensitive code that uses `epoch_timestamp_ms` (previous code): +### Testing Epoch-Sensitive Code +Tests based on [`iota::test_scenario`] can use `later_epoch` to exercise time-sensitive code that uses `epoch_timestamp_ms`: ```move file=/crates/iota-framework/packages/iota-framework/sources/test/test_scenario.move#L141-L148 ``` -`later_epoch` behaves like `iota::test_scenario::next_epoch` (finishes the current transaction and epoch in the test scenario), but also increments the timestamp by `delta_ms` milliseconds to simulate the progress of time. +The `later_epoch` function behaves like `iota::test_scenario::next_epoch` (finishes the current transaction and epoch in the test scenario) +but also increments the timestamp by `delta_ms` milliseconds to simulate the passage of time. - \ No newline at end of file + diff --git a/docs/content/developer/iota-101/create-coin/create-coin.mdx b/docs/content/developer/iota-101/create-coin/create-coin.mdx index c8f45981a1c..7964b35247e 100644 --- a/docs/content/developer/iota-101/create-coin/create-coin.mdx +++ b/docs/content/developer/iota-101/create-coin/create-coin.mdx @@ -1,33 +1,47 @@ --- -title: Create Coins and Tokens +description: Learn how to create coins and tokens on the IOTA blockchain using Move. --- import Quiz from '@site/src/components/Quiz'; import questions from '/json/developer/iota-101/create-coin/create-coin.json'; +# Creating Coins and Tokens -Coins and tokens on IOTA are similar. In practice, the terms are used interchangeably, but there are some differences in their implementation. You can learn about these differences in the respective standard documentation, [Closed-Loop Token](../../standards/closed-loop-token.mdx) and [Coin](../../standards/coin.mdx). +Coins and tokens in IOTA are similar concepts, often used interchangeably, +but there are subtle differences in their implementation. +To understand these differences, refer to the standard documentation for [Closed-Loop Token](../../standards/closed-loop-token.mdx) and [Coin](../../standards/coin.mdx). -Publishing a coin on IOTA is nearly as straightforward as publishing a new type. The main difference is the requirement of a [one-time witness](../move-overview/one-time-witness.mdx) when creating a coin. +## Publishing a Coin + +Publishing a coin on IOTA is almost as straightforward as publishing a new type. +The key difference is the requirement of a [one-time witness](../move-overview/one-time-witness.mdx) when creating a coin. ```move file=/examples/move/coin/sources/my_coin.move ``` -The `Coin` is a generic implementation of a coin on IOTA. Access to the `TreasuryCap` provides control over the minting and burning of coins. Further transactions can be sent directly to the `iota::coin::Coin` with `TreasuryCap` object as authorization. +The [`Coin`](../../../references/framework/iota-framework/coin.mdx) is a generic coin implementation in IOTA. +By accessing the [`TreasuryCap`](../../../references/framework/iota-framework/coin.mdx#resource-treasurycap), +you gain control over minting and burning coins. +You can send further transactions directly to `iota::coin::Coin` using the `TreasuryCap` object for authorization. -Extending the example further, add a `mint` function to the module. Use the `mint` function of the `Coin` module to create (mint) a coin and then transfer it to an address. +## Extending the Coin Module -```move file=/examples/move/coin/sources/my_coin.move#L23-L32 -``` +To extend the coin module, add a `mint` function. +This function utilizes the [`mint`](../../../references/framework/iota-framework/coin.mdx#function-mint) method from the `Coin` module +to create a coin and transfer it to a specified address. -### IOTA CLI +## IOTA CLI -If you published the previous example to a IOTA network, you can use the `iota client call` command to mint coins and deliver them to the address you provide. See [IOTA CLI](../../../references/cli.mdx) for more information on the command line interface. +### Minting Coins + +After publishing the coin module to the IOTA network, +you can mint coins and send them to an address using the `iota client call` command. +For more details on the command-line interface, see [IOTA CLI](../../../references/cli.mdx). ```shell iota client call --function mint --module mycoin --package --args ``` -If the call is successful your console displays the result, which includes a **Balance Changes** section with the following information included: +Upon successful execution, the console displays output including a **Balance Changes** section: ```shell ... @@ -39,34 +53,40 @@ Amount: ... ``` -## DenyList - -See [`DenyList`](./regulated.mdx#denylist). +## Implementing a Deny List -## Create regulated coin +If you need to restrict specific addresses from accessing your coin, consider implementing a [`DenyList`](./regulated.mdx#managing-the-deny-list). -If you need the ability to deny specific addresses from having access to your coin, you can use the `create_regulated_currency` function (instead of `create_currency`) to create it. +## Creating a Regulated Coin -Behind the scenes, `create_regulated_currency` uses the `create_currency` function to create the coin, but also produces a `DenyCap` object that allows its bearer to control access to the coin's deny list in a `DenyList` object. Consequently, the way to create a coin using `create_regulated_currency` is similar to the previous example, with the addition of a transfer of the `DenyCap` object to the module publisher. +To deny specific addresses from holding your coin, +use the [`create_regulated_currency`](../../../references/framework/iota-framework/coin.mdx#function-create_regulated_currency_v1) function instead of [`create_currency`](../../../references/framework/iota-framework/coin.mdx#function-create_currency). -## Create tokens +Internally, `create_regulated_currency` calls `create_currency` to create the coin +and also produces a [`DenyCap`](../../../references/framework/iota-framework/coin.mdx#resource-denycapv1) object. +This object allows you to manage the deny list in a `DenyList` object. +The process is similar to the previous example but includes transferring the `DenyCap` object to the module publisher. -Tokens reuse the `TreasuryCap` defined in the `iota::coin` module and therefore have the same initialization process. The `coin::create_currency` function guarantees the uniqueness of the `TreasuryCap` and forces the creation of a `CoinMetadata` object. +## Creating Tokens -Coin-like functions perform the minting and burning of tokens. Both require the `TreasuryCap`: +Tokens reuse the [`TreasuryCap`](../../../references/framework/iota-framework/coin.mdx#resource-treasurycap) defined in the `iota::coin` module +and follow the same initialization process. +The `coin::create_currency` function ensures the uniqueness of the `TreasuryCap` and enforces the creation of a `CoinMetadata` object. -- `token::mint` - mint a token -- `token::burn` - burn a token +You can mint and burn tokens using functions similar to those for coins, both requiring the `TreasuryCap`: -See [Closed-Loop Token](../../standards/closed-loop-token.mdx) standard for complete details of working with tokens. +- [`token::mint`](../../../references/framework/iota-framework/token.mdx#function-mint) — Mint a token. +- [`token::burn`](../../../references/framework/iota-framework/token.mdx#function-burn) — Burn a token. -## Examples +For complete details on working with tokens, refer to the [Closed-Loop Token](../../standards/closed-loop-token.mdx) standard. -See the following topics for examples of some common use cases for coin and token creation. +## Additional Examples -- [Regulated Coin and Deny List](regulated.mdx): Create a regulated coin and add or remove names from the deny list. -- [Loyalty Token](loyalty.mdx): Create a token to reward user loyalty. -- [In-Game Token](in-game-token.mdx): Create tokens that can be used only within a mobile game. +Explore these topics for practical examples of coin and token creation: +- [Migrate to CoinManager](migrate-to-coin-manager.mdx): Learn about IOTA's unique [`CoinManager`](../../../references/framework/iota-framework/coin_manager.mdx), and how it simplify managing your [Coins](../../../references/framework/iota-framework/coin.mdx). +- [Regulated Coin and Deny List](regulated.mdx): Learn how to create a regulated coin and manage the deny list. +- [Loyalty Token](loyalty.mdx): Discover how to create a token to reward user loyalty. +- [In-Game Token](in-game-token.mdx): Understand how to create tokens usable within a mobile game. \ No newline at end of file diff --git a/docs/content/developer/iota-101/create-coin/in-game-token.mdx b/docs/content/developer/iota-101/create-coin/in-game-token.mdx index e252bfdbfc8..d73b84420e5 100644 --- a/docs/content/developer/iota-101/create-coin/in-game-token.mdx +++ b/docs/content/developer/iota-101/create-coin/in-game-token.mdx @@ -1,43 +1,67 @@ --- -title: In-Game Currency +description: Learn how to create in-game currency on IOTA using the Closed-Loop Token standard. --- import Quiz from '@site/src/components/Quiz'; import questions from '/json/developer/iota-101/create-coin/in-game-token.json'; +# Creating In-Game Currency -Using the IOTA [Closed-Loop Token](../../standards/closed-loop-token.mdx) standard, you can create in-game currency (such as gems or diamonds in mobile games) that you can grant to players for their actions or make available to purchase. You mint the tokens on IOTA, but players can only use the tokens within the economy of the game itself. These types of tokens are usually not transferrable and you would typically mint them in predefined amounts to maintain scarcity and game balance. +You can use the IOTA [Closed-Loop Token](../../standards/closed-loop-token.mdx) standard +to develop in-game currencies like gems or diamonds commonly found in mobile games. +These tokens can be awarded to players for their actions or made available for purchase. +While minted on the IOTA network, players can only use these tokens within the game's ecosystem. +Typically, such tokens are non-transferable and are minted in predefined quantities to maintain scarcity and balance within the game. -The following example creates an in-game currency called a GEM, which represents a certain number of IOTA. In the example, the user can buy fungible GEMs using IOTA, which can then be used as currency within the game. Use the code comments to follow the logic of the example. +## Setting Up the GEM Currency -## Example +In the following example creates an in-game currency called `GEM`, representing a specific amount of IOTA. +Players can purchase fungible `GEMs` using IOTA, which they can then spend within the game. -The IOTA repo hosts a basic example of creating in-game currency. The Move modules that create the economy of the example are in the gems.move source file. +### Example Overview -### Module examples::sword +The IOTA repository includes a [basic example of creating an in-game currency]https://github.com/iotaledger/iota/tree/develop/examples/move/token. +The Move modules responsible for establishing the game's economy are located in the [`gems.move`](https://github.com/iotaledger/iota/blob/develop/examples/move/token/sources/gems.move) source file. -The `examples::sword` module creates one of the objects, a `sword`, that has in-game value. The module assigns a value in GEMs (the other valuable in-game item) to the sword. The module also provides the logic for trading GEMs to receive a sword. +### The `examples::sword` Module + +The [`examples::sword` module](https://github.com/iotaledger/iota/blob/develop/examples/move/token/sources/gems.move#L8) defines an in-game object, a `sword`, +which holds value within the game. +This module assigns a `GEM` value to the sword and includes the logic for trading `GEMs` to acquire a sword. ```move file=/examples/move/token/sources/gems.move#L8-L32 ``` -### Module examples::gem +### The `examples::gem` Module + +The [`examples::gem` module](https://github.com/iotaledger/iota/blob/develop/examples/move/token/sources/gems.move#L36) is responsible +for creating the `GEM` in-game currency. +Players spend IOTA to purchase `GEMs`, which they can trade for swords or other in-game items. +The module defines three tiers of `GEM` packages—small, medium, and large—each representing different in-game values. +Constants within the module specify both the value and the quantity of GEMs in each package. + +```move file=/examples/move/token/sources/gems.move#L73-L69 +``` -The `examples::gem` module creates the in-game currency, GEMs. Users spend IOTA to purchase GEMs, which can then be traded for swords. The module defines three groups of GEMs (small, medium, and large), with each group representing a different in-game value. Constants hold both the value of each package and the actual number of GEMs the groups contain. +#### Initializing the GEM Currency -The module's `init` function uses `coin::create_currency` to create the GEM. The `init` function, which runs only the one time when the module publishes, also sets the policies for the in-game currency, freezes the metadata for the coin, and transfers the policy capability to the publisher of the package. +The `init` function in the module uses `coin::create_currency` to create the GEM currency. This function runs only once upon module publication. It sets the policies for the in-game currency, freezes the coin's metadata, and transfers the policy capability to the package publisher. ```move file=/examples/move/token/sources/gems.move#L76-L100 ``` -The module handles the purchase of GEMs with the `buy_gems` function. +#### Purchasing GEMs + +The module handles the purchase of `GEMs` through the `buy_gems` function. ```move file=/examples/move/token/sources/gems.move#L104-L125 ``` +## Viewing the Complete Module Code + +For a comprehensive understanding, you can view the complete code of the `gems.move` module below. +
- -Toggle complete module code - +Click to expand the full module code ```move file=/examples/move/token/sources/gems.move ``` diff --git a/docs/content/developer/iota-101/create-coin/loyalty.mdx b/docs/content/developer/iota-101/create-coin/loyalty.mdx index f279e89ab43..fb109ccb178 100644 --- a/docs/content/developer/iota-101/create-coin/loyalty.mdx +++ b/docs/content/developer/iota-101/create-coin/loyalty.mdx @@ -1,49 +1,77 @@ --- -title: Loyalty Tokens +description: Learn how to create loyalty tokens on IOTA for use in digital services. --- import Quiz from '@site/src/components/Quiz'; import questions from '/json/developer/iota-101/create-coin/loyalty.json'; -Using the IOTA [Closed-Loop Token](../../standards/closed-loop-token.mdx) standard, you can create tokens that are valid only for a specific service, like an airline that wants to grant tokens to frequent flyers to purchase tickets or upgrades. +# Creating Loyalty Token -The following example demonstrates the creation of a loyalty token that bearers can use to make purchases in a digital gift shop. +You can use the IOTA [Closed-Loop Token](../../standards/closed-loop-token.mdx) standard +to create [tokens](../../../references/framework/iota-framework/token.mdx) that are valid only within a specific service. +For example, an airline might grant tokens to frequent flyers that they can use to purchase tickets or upgrades. -## Example +In this guide, you'll learn how to create a loyalty token that users can use to make purchases in a digital gift shop. -The Loyalty Token example illustrates a loyalty token that is created with the Closed Loop Token standard. If you were to implement this example, the Admin would send `LOYALTY` tokens to the users of your service as a reward for their loyalty. The example creates a `GiftShop` where holders can spend `LOYALTY` tokens to buy `Gift`s. +## Overview -### examples::loyalty +The following example demonstrates how to create a loyalty token using the Closed-Loop Token standard. +As the administrator, you would send `LOYALTY` tokens to your service's users as a reward for their loyalty. +The example includes a `GiftShop` where holders can spend `LOYALTY` tokens to buy `Gift` items. -The loyalty.move source file contains the `examples::loyalty` module code that creates the loyalty token. The module includes the one-time witness (OTW) that creates the coin (with the same name as the module, `LOYALTY`), possesses only the `drop` ability, and has no fields. These are the characteristics of a OTW, which ensures the `LOYALTY` type has a single instance. +## Module: `examples::loyalty` + +The `examples::loyalty` module, found in the `loyalty.move` source file, contains the code to create the loyalty token. +The module defines a [one-time witness (OTW)](../move-overview/one-time-witness.mdx) +that creates the coin named `LOYALTY`. +This coin possesses only the `drop` ability and has no fields. +These characteristics ensure the `LOYALTY` type has a single instance. ```move file=/examples/move/token/sources/loyalty.move#L22-L23 ``` -The `init` function of the module uses the `LOYALTY` OTW to create the token. All `init` functions run one time only at the package publish event. The initializer function makes use of the OTW `LOYALTY` type defined previously in its call to `create_currency`. The function also defines a policy, sending both the policy capability and trasury capability to the address associated with the publish event. The holder of these transferrable capabilities can mint new `LOYALTY` tokens and modify their policies. +### Initialization Function + +The module's [`init` function](../move-overview/init.mdx) uses the `LOYALTY` OTW to create the token. +Remember that all `init` functions run only once at the package publish event. +The initializer function calls [`create_currency`](../../../references/framework/iota-framework/coin.mdx#function-create_currency) +using the `LOYALTY` type defined earlier. +It also sets up a policy by sending both the [policy capability](../../../references/framework/iota-framework/token.mdx#resource-tokenpolicycap) +and the [treasury capability](../../../references/framework/iota-framework/coin.mdx#resource-treasurycap) to the address associated with the publish event. +The holder of these transferable capabilities can mint new `LOYALTY` tokens and modify their policies. ```move file=/examples/move/token/sources/loyalty.move#L37-L63 ``` -The `LOYALTY` minting function is called `reward_user`. As mentioned previously, the holder of the `TreasuryCap` can call this function to mint new loyalty tokens and send them to the desired address. The function uses the `token::mint` function to create the token and `token::transfer` to send it to the intended recipient. +### Minting Function: `reward_user` + +The `reward_user` function allows the holder of the `TreasuryCap` +to mint new loyalty tokens and send them to specified addresses. +It uses the [`token::mint`](../../../references/framework/iota-framework/token.mdx#function-mint) function +to create the tokens and [`token::transfer`](../../../references/framework/iota-framework/token.mdx#function-transfer) to deliver them to the intended recipients. ```move file=/examples/move/token/sources/loyalty.move#L71-L81 ``` -Finally, the example includes a `buy_a_gift` function to handle the redemption of `LOYALTY` tokens for `Gift` types. The function ensures the gift price matches the number of loyalty tokens spent, then uses the `token::spend` function to handle the treasury bookkeeping. +#### Redeeming Tokens: `buy_a_gift` +Finally, the module includes a `buy_a_gift` function to handle the redemption of `LOYALTY` tokens for `Gift` items. +This function ensures that the gift's price matches the number of loyalty tokens spent. +It uses the [`token::spend`](../../../references/framework/iota-framework/token.mdx#function-spend) function to manage the treasury bookkeeping. ```move file=/examples/move/token/sources/loyalty.move#L85-L100 ``` +## Full Source Code + +For a complete view of the module, you can review the full source code below. +
- -Toggle complete source code - +Click to view the complete source code ```move file=/examples/move/token/sources/loyalty.move ```
- + \ No newline at end of file diff --git a/docs/content/developer/iota-101/create-coin/regulated.mdx b/docs/content/developer/iota-101/create-coin/regulated.mdx index a2c9b512619..f51e4419559 100644 --- a/docs/content/developer/iota-101/create-coin/regulated.mdx +++ b/docs/content/developer/iota-101/create-coin/regulated.mdx @@ -1,13 +1,21 @@ --- -title: Regulated Coin and Deny List -description: You can create regulated coins on IOTA, such as stablecoins. These coins are similar to other coins like IOTA, but include the ability to control access to the coin using a deny list. +description: Learn how to create regulated coins on IOTA using deny lists for access control. --- import Quiz from '@site/src/components/Quiz'; import questions from '/json/developer/iota-101/create-coin/regulated.json'; -The IOTA [Coin](../../standards/coin.mdx) standard provides a `create_regulated_currency` function to create coins. This function is different than `create_currency` in that it generates a coin that you can block certain addresses from being able to use those coins in transactions. This ability is a requirement for assets like stablecoins. +# Creating Regulated Coins with Deny Lists on IOTA -Behind the scenes, `create_regulated_currency` uses the `create_currency` function to create the coin, but also produces a `DenyCap` object that allows its bearer to control access to the coin's deny list in a `DenyList` object. Consequently, the way to create a coin using `create_regulated_currency` is similar to the previous example, with the addition of a transfer of the `DenyCap` object to the module publisher. +Regulated coins on IOTA allow you to control access by restricting certain addresses, +which is crucial for assets like stablecoins. +These coins are similar to standard IOTA coins but provide additional control through a deny list. + +The IOTA [Coin](../../standards/coin.mdx) standard offers the [`create_regulated_currency`](../../../references/framework/iota-framework/coin.mdx#function-create_regulated_currency_v1) function +to create such coins. +This function not only creates the coin but also generates a [`DenyCap`](../../../references/framework/iota-framework/coin.mdx#resource-denycapv1) object, +\enabling you to manage a deny list via a [`DenyList`](../../../references/framework/iota-framework/deny_list.mdx) object. +The process is akin to using [`create_currency`](../../../references/framework/iota-framework/coin.mdx#function-create_currency), +with the added step of handling the `DenyCap` object. ```move title="regcoin.move" module examples::regcoin { @@ -24,7 +32,7 @@ module examples::regcoin { } ``` -When you deploy the previous module using `iota client publish`, the console responds with transaction effects, including the creation of the following objects: +Deploying this module with `iota client publish` results in transaction effects that include the creation of several objects: ```shell ... @@ -72,43 +80,52 @@ Created Objects: ... ``` -As you might have noticed, the publish action creates a `RegulatedCoinMetadata` object along with the standard `CoinMetadata` object. You don't need to explicitly call the `freeze_object` on the `RegulatedCoinMetadata` object, however, because `create_regulated_currency` automatically performs this action. +Notice that a [`RegulatedCoinMetadata`](../../../references/framework/iota-framework/coin.mdx#resource-regulatedcoinmetadata) object +is created alongside the standard [`CoinMetadata`](../../../references/framework/iota-framework/coin.mdx#resource-coinmetadata) object. +You don't need to explicitly freeze the `RegulatedCoinMetadata` object because `create_regulated_currency` does this automatically. + +The output also shows three objects now owned by the publisher: -The output also shows the three objects that the publisher now owns: `UpgradeCap` for [package upgrades](../move-overview/package-upgrades/upgrade.mdx), `TreasuryCap` for minting or burning coins, and the `DenyCap` for adding or removing addresses to or from the deny list for this coin. +- [`UpgradeCap`](../../../references/framework/iota-framework/package.mdx#resource-upgradecap) for [package upgrades](../move-overview/package-upgrades/upgrade.mdx). +- [`TreasuryCap`](../../../references/framework/iota-framework/coin.mdx#resource-treasurycap) for minting or burning coins. +- [`DenyCap`](../../../references/framework/iota-framework/coin.mdx#resource-denycapv1) for managing the deny list associated with this coin. -## DenyList +## Understanding the DenyList -The IOTA framework provides a `DenyList` singleton, shared object that the bearer of a `DenyCap` can access to specify a list of addresses that are unable to use a IOTA core type. The initial use case for `DenyList`, however, focuses on limiting access to coins of a specified type. This is useful, for example, when creating a regulated coin on IOTA that requires the ability to block certain addresses from using it as inputs to transactions. Regulated coins on IOTA satisfy any regulations that require the ability to prevent known bad actors from having access to those coins. +The IOTA framework provides a `DenyList` singleton shared object, identified by the address `0x403`. +Holders of a `DenyCap` can specify addresses prohibited from using certain IOTA core types, +which is particularly useful for regulated coins needing to comply with regulations. :::info -The `DenyList` object is a system object that has the address `0x403`. You cannot create it yourself. +You cannot create the `DenyList` object yourself; it is a system object at address `0x403`. ::: +### Managing the Deny List -### Manipulate deny list - -For the ability to manipulate the addresses assigned to the deny list for your coin, you must add a few functions to the previous example. +To manipulate the deny list for your coin, add these functions to your module: ```move -public fun add_addr_from_deny_list(denylist: &mut DenyList, denycap: &mut DenyCap, denyaddy: address, ctx: &mut TxContext){ - coin::deny_list_add(denylist, denycap, denyaddy, ctx ); +public fun add_address_to_deny_list(denylist: &mut DenyList, denycap: &mut DenyCap, address_to_deny: address, ctx: &mut TxContext){ + coin::deny_list_add(denylist, denycap, address_to_deny, ctx); } -public fun remove_addr_from_deny_list(denylist: &mut DenyList, denycap: &mut DenyCap, denyaddy: address, ctx: &mut TxContext){ - coin::deny_list_remove(denylist, denycap, denyaddy, ctx ); +public fun remove_address_from_deny_list(denylist: &mut DenyList, denycap: &mut DenyCap, address_to_allow: address, ctx: &mut TxContext){ + coin::deny_list_remove(denylist, denycap, address_to_allow, ctx); } ``` -To use these functions, you pass the `DenyList` object (`0x403`), your `DenyCap` object ID, and the address you want to either add or remove. Using the IOTA CLI, you could use `iota client call` with the required information: +To use these functions, pass the `DenyList` object (`0x403`), your `DenyCap` object ID, and the address you wish to add or remove. + +Using the IOTA CLI: ```shell -iota client call --function add_addr_from_deny_list --module regcoin --package --args +iota client call --function add_address_to_deny_list --module regcoin --package --args Transaction Digest: ``` -The console displays the response from the network, where you can verify the `DenyList` object is mutated. +The console will display the network response, confirming that the `DenyList` object has been mutated: ```shell ... @@ -123,9 +140,8 @@ MutatedObjects: Digest: ... - ``` -For all `Coin` functions available, see the IOTA framework [`coin` module documentation](/references/framework/iota-framework/coin.mdx). +For a complete list of available `Coin` functions, refer to the IOTA framework [`coin` module documentation](../../../references/framework/iota-framework/coin.mdx). \ No newline at end of file diff --git a/docs/content/developer/iota-101/using-events.mdx b/docs/content/developer/iota-101/using-events.mdx index fdedcd9b607..3b00a9ac9ae 100644 --- a/docs/content/developer/iota-101/using-events.mdx +++ b/docs/content/developer/iota-101/using-events.mdx @@ -1,16 +1,22 @@ --- -title: Using Events -description: Monitor IOTA on-chain activity by subscribing to events that Move packages published on IOTA emit. +description: Learn how to monitor IOTA on-chain activity by subscribing to events emitted by Move packages. --- import AlphaNet from "../../_snippets/alphanet.mdx"; import Quiz from '@site/src/components/Quiz'; import questions from '/json/developer/iota-101/using-events.json'; -The IOTA network stores countless objects on chain where Move code can perform actions using those objects. Tracking this activity is often desired, for example, to discover how many times a module mints an NFT or to tally the amount of IOTA in transactions that a smart contract generates. +# Subscribing to On-Chain Events in IOTA -To support activity monitoring, Move provides a structure to emit events on the IOTA network. When you establish a connection with the IOTA network, you create a two-way interactive communication session between your client and a IOTA network node. With an open session, you can subscribe to specific events that the IOTA network adds to the stream to create real-time monitoring of events. +Monitoring on-chain activity is essential for understanding and reacting to actions performed by smart contracts on the IOTA network. +By subscribing to events emitted by Move packages, you can track activities such as NFT minting or IOTA transactions in real-time. +This guide will show you how to emit events in Move and subscribe to them using the IOTA network. -## Move Event Structure +## Understanding Events in IOTA + +Events in IOTA provide a structured way to capture and broadcast on-chain activities. +Each event contains specific attributes that offer detailed information about what occurred. + +### Event Structure An event object in IOTA consists of the following attributes: @@ -23,23 +29,26 @@ An event object in IOTA consists of the following attributes: - `bcs`: Binary canonical serialization value. - `timestampMs`: Unix epoch timestamp in milliseconds. -## Discovering Events +## Exploring Available Events -If you want to subscribe to events on chain, you first need to know what events are available. You typically know or can discover the events your own code emits, but it's not as straightforward when you need to subscribe to on-chain events from packages you don't own. The IOTA RPC provides a [queryEvents](/iota-api-ref#iotax_queryevents) method to query on-chain packages and return available events that you can subscribe to. +To subscribe to on-chain events, you first need to identify which events are available. While you can easily track events emitted by your own code, discovering events from external packages can be more challenging. The IOTA RPC provides the [`queryEvents`](/iota-api-ref#iotax_queryevents) method, which allows you to query on-chain packages and obtain a list of events you can subscribe to. -## Filter Events +## Applying Event Filters -You can filter the events your code targets for either querying or subscribing. Both filter options are similar but have some differences. +When targeting specific events for querying or subscribing, you can use filters to refine your results. Although the filtering options for querying and subscribing are similar, there are notable differences to be aware of. -## Emit Events in Move +## Emitting Events in Move -To create an event in your Move modules, add the `iota::event` dependency. +To emit events from your Move modules, you need to use the [`iota::event`](../../references/framework/iota-framework/event.mdx) module. +Emitting events allows external applications to subscribe and respond to specific on-chain activities. + +First, import the `event` module in your Move code: ```move use iota::event; ``` -With the dependency added, you can use the `emit` function to fire an event whenever the action you want to monitor fires. For example, the following code is part of an example application using digital donuts. The `collect_profits` function handles the collection of IOTA and emits an event whenever the function is called. To act on that event, you need to subscribe to it. +Then, within your function, you can emit an event using the [`emit`](../../references/framework/iota-framework/event.mdx#function-emit) function. For example: ```move /// Take coin from `DonutShop` and transfer it to tx sender. @@ -53,11 +62,12 @@ public fun collect_profits( _: &ShopOwnerCap, shop: &mut DonutShop, ctx: &mut Tx } ``` -## Subscribe to Events in Move +## Subscribing to Events -Firing events is not very useful in a vacuum. You also need the ability to listen for those events so that you can act on them. In IOTA, you subscribe to those events and provide logic that triggers when the event fires. +To react to events emitted by Move modules, you need to subscribe to them. +IOTA full nodes support event subscriptions via JSON-RPC notifications over WebSocket. You can interact with the [RPC directly][iotax_subscribeEvent](/iota-api-ref#iotax_subscribeevent), [iotax_subscribeTransaction](/iota-api-ref#iotax_subscribetransaction) or use an SDK like the [IOTA TypeScript SDK](../../references/ts-sdk/typescript/index.mdx). -IOTA Full nodes support subscribe functionality using JSON-RPC notifications transmitted through the WebSocket API. You can interact with the RPC directly ([iotax_subscribeEvent](/iota-api-ref#iotax_subscribeevent), [iotax_subscribeTransaction](/iota-api-ref#iotax_subscribetransaction)) or you can use an SDK like the IOTA TypeScript SDK. The following excerpt from one of the examples uses the TypeScript SDK to create an asynchronous subscription to the filter identified in the filter. +The following excerpt from one of the examples uses the TypeScript SDK to create an asynchronous subscription to the filter identified in the filter. ```move let unsubscribe = await provider.subscribeEvent({ @@ -90,7 +100,7 @@ Move smart contracts can call other smart contracts that emit events. For exampl ## Examples -## Subscribe to Event +### Subscribe to Event This example leverages the IOTA TypeScript SDK to subscribe to events the package with ID `` emits. Each time the event fires, the code displays the response to the console. @@ -192,9 +202,9 @@ subscribeEvent { -## Filtering Event Queries +## Filtering Events -To filter the events returned from your queries, use the following data structures. +You can filter events when querying or subscribing to receive only the events you are interested in. :::info @@ -202,6 +212,9 @@ This set of filters applies only to event querying APIs. It differs from the fil ::: +### Filtering Event Queries + +When querying events, use the following filters: | Query | Description | JSON-RPC Parameter Example | | ------------- | -------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | @@ -216,9 +229,9 @@ This set of filters applies only to event querying APIs. It differs from the fil | `Object` | Return events associated with the given object | `{"Object":"0x727b37454ab13d5c1dbb22e8741bff72b145d1e660f71b275c01f24e7860e5e5"}` | | `TimeRange` | Return events emitted in [start_time, end_time] interval | `{"TimeRange":{"startTime":1669039504014, "endTime":1669039604014}}` | -## Filtering Events for Subscription +### Filtering Events for Subscription -To create a subscription, you can set a filter to return only the set of events you're interested in listening for. Unlike filtering event queries, you are able to combine subscription filters. +When subscribing to events, you can combine filters for more precise results: | Filter | Description | JSON-RPC Parameter Example | | ----------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------ | diff --git a/docs/content/developer/stardust/exchanges.mdx b/docs/content/developer/stardust/exchanges.mdx index 150d636cfb4..a57ba49711a 100644 --- a/docs/content/developer/stardust/exchanges.mdx +++ b/docs/content/developer/stardust/exchanges.mdx @@ -41,7 +41,7 @@ The most common use case for exchanges and custody providers regarding integrati - All `Coin` objects can be freely transferred by the owner of this object. - Only the owner of the `Coin` object can interact with it. -- The only restriction that can be added to a `Coin` object is the optional blocking of transfers for addresses on a [`DenyList`](../iota-101/create-coin/regulated.mdx#denylist). This only applies to `Coin` objects that have been instantiated as [Regulated Coins](../standards/coin.mdx#regulated-coins). +- The only restriction that can be added to a `Coin` object is the optional blocking of transfers for addresses on a [`DenyList`](../iota-101/create-coin/regulated.mdx#managing-the-deny-list). This only applies to `Coin` objects that have been instantiated as [Regulated Coins](../standards/coin.mdx#regulated-coins). - It is not possible to add other limiting functionality to `Coin` objects directly, like vesting or time-locks; this can only be done by wrapping the unrestricted `Coin` object within another restricting object. It's safe to assume that if you receive a `Coin` object, you can use it without limitations. - A `Coin` is tied to a `CoinMetadata` object containing `name`, `symbol`, `decimals`, `description`, and an `icon_url`. - The holder of the TreasuryCap handles administrative tasks of a Coin like minting new tokens or changing metadata; without a `TreasuryCap` these actions can no longer be performed. diff --git a/docs/content/references/ts-sdk/kiosk/advanced-examples.mdx b/docs/content/references/ts-sdk/kiosk/advanced-examples.mdx index 15e0100bebc..4a5006f01fb 100644 --- a/docs/content/references/ts-sdk/kiosk/advanced-examples.mdx +++ b/docs/content/references/ts-sdk/kiosk/advanced-examples.mdx @@ -7,10 +7,10 @@ For these examples, assume you have the following data and functions available: ```typescript -// a constant for bullshark's type. -const bullsharkType = `${packageId}::iotafrens::IotaFren<${packageId}::bullshark::Bullshark>`; -// a constant for capy's type. -const capyType = `${packageId}::iotafrens::IotaFren<${packageId}::capy::Capy>`; +// a constant for my type. +const myType = `${packageId}::my_module::MyStruct<${packageId}::my_coin_module::MyCoin>`; +// a constant for another type. +const otherType = `${packageId}::other_module::OtherStruct<${packageId}::other_coin_module::OtherCoin>`; // initialize a kioskClient. const kioskClient = new KioskClient({ @@ -21,12 +21,12 @@ const kioskClient = new KioskClient({ }); ``` -## Minting a IotaFren +## Minting a MyCoin -This example demonstrates how to mint a IotaFren. +This example demonstrates how to mint a MyCoin. ```typescript -async function mintFren(address: string) { +async function mintMyCoin(address: string) { const { kioskOwnerCaps } = await kioskClient.getOwnedKiosks({ address }); // Choose the first kiosk for simplicity. We could have extra logic here (e.g. let the user choose, pick a personal one, etc). @@ -38,11 +38,11 @@ async function mintFren(address: string) { // We're mixing the logic here. If the cap is undefined, we create a new kiosk. if (!cap) kioskTx.create(); - // Let's mint a capy here into the kiosk (either a new or an existing one). + // Let's mint a MyCoin here into the kiosk (either a new or an existing one). txb.moveCall({ - target: `${packageId}::iotafrens::mint_app::mint`, + target: `${packageId}::my_module::mint_app::mint`, arguments: [kioskTx.getKiosk(), kioskTx.getKioskCap()], - typeArguments: [capyType], + typeArguments: [myType], }); // If we don't have a cap, that means we create a new kiosk for the user in this flow. @@ -55,47 +55,47 @@ async function mintFren(address: string) { } ``` -## Mixing two Iotafrens +## Mixing two MyCoins -This example demonstrates how to use the Kiosk SDK to mix two `bullsharks`. +This example demonstrates how to use the Kiosk SDK to mix two `MyCoins`. ```typescript -// We're mixing two frens. -async function mixFrens(firstFrenObjectId: string, secondFrenObjectId: string, cap: KioskOwnerCap) { +// We're mixing two coins. +async function mixMyCoins(firstCoinObjectId: string, secondCoinObjectId: string, cap: KioskOwnerCap) { const txb = new TransactionBlock(); const kioskTx = new KioskTransaction({ transactionBlock: txb, kioskClient, cap }); - // borrow both frens. - const [fren1, promise1] = kioskTx.borrow({ - itemType: bullsharkType, - itemId: firstFrenObjectId. + // borrow both coins. + const [coin1, promise1] = kioskTx.borrow({ + itemType: myType, + itemId: firstCoinObjectId. }); - const [fren2, promise2] = kioskTx.borrow({ - itemType: bullsharkType, - itemId: secondFrenObjectId. + const [coin2, promise2] = kioskTx.borrow({ + itemType: myType, + itemId: secondCoinObjectId. }); // Let's call the mix function. We skip any payment related stuff here. txb.moveCall({ target: `${packageId}::mix_app::mix`, arguments: [ - fren1, - fren2, + coin1, + coin2, kioskTx.getKiosk(), kioskTx.getKioskCap(), ] - typeArguments: [bullsharkType], + typeArguments: [myType], }); kioskTx.return({ - itemType: bullsharkType, - item: fren1, + itemType: myType, + item: coin1, promise: promise1 }) .return({ - itemType: bullsharkType, - item: fren2, + itemType: myType, + item: coin2, promise: promise2 }).finalize(); diff --git a/docs/content/references/ts-sdk/typescript/owned-object-pool/custom-split-strategy.mdx b/docs/content/references/ts-sdk/typescript/owned-object-pool/custom-split-strategy.mdx index 0da11bff4ba..e46f034c3ab 100644 --- a/docs/content/references/ts-sdk/typescript/owned-object-pool/custom-split-strategy.mdx +++ b/docs/content/references/ts-sdk/typescript/owned-object-pool/custom-split-strategy.mdx @@ -16,13 +16,13 @@ always be able to pay for the gas of the transaction. However, in more complex scenarios, you might want to define your own split strategy. -Assume that you want to execute multiple transactions that transfer an object of type `CapyNFT`, +Assume that you want to execute multiple transactions that transfer an object of type `MyNFT`, each to a different recipient. For this to work, the `ExecutorServiceHandler` needs to split the `mainPool` in a way such that every worker: -- Contains at least one `CapyNFT` object. +- Contains at least one `MyNFT` object. - Contains at least a coin (or set of coins) with a total balance enough to pay for the gas of the transaction. @@ -30,7 +30,7 @@ To do this, you have to implement the `SplitStrategy` interface. In detail: ```ts class MyCustomSplitStrategy implements SplitStrategy { - private capyIncluded = false; + private myNftIncluded = false; private balanceSoFar = 0; private readonly minimumBalance; @@ -38,13 +38,13 @@ class MyCustomSplitStrategy implements SplitStrategy { if (!obj) throw new Error('No object found!.'); // If each requirement is fulfilled then terminate the split by returning null // This stops the split process and the worker pool is created - const terminateWhen = this.balanceSoFar >= this.minimumBalance && this.capyIncluded; + const terminateWhen = this.balanceSoFar >= this.minimumBalance && this.myNftIncluded; if (terminateWhen) { return null; } - // If a CapyNFT object is not already included, and the object is a CapyNFT, then include it - if (!capyIncluded && obj.type.includes('CapyNFT')) { - this.capyIncluded = true; + // If a MyNFT object is not already included, and the object is a MyNFT, then include it + if (!myNftIncluded && obj.type.includes('MyNFT')) { + this.myNftIncluded = true; return true; } // If the object is a coin and coins are still needed, then include it to the new pool diff --git a/docs/site/static/json/developer/iota-101/create-coin/in-game-token.json b/docs/site/static/json/developer/iota-101/create-coin/in-game-token.json index 0cd49983aef..56c938a6766 100644 --- a/docs/site/static/json/developer/iota-101/create-coin/in-game-token.json +++ b/docs/site/static/json/developer/iota-101/create-coin/in-game-token.json @@ -13,7 +13,7 @@ "answerOptions": [ { "answerText": "IOTA", "isCorrect": false }, { "answerText": "GEM", "isCorrect": true }, - { "answerText": "Capy", "isCorrect": false }, + { "answerText": "Doge", "isCorrect": false }, { "answerText": "Swords", "isCorrect": false } ] } diff --git a/examples/move/token/sources/gems.move b/examples/move/token/sources/gems.move index 6ca17df83ae..643ed42d63b 100644 --- a/examples/move/token/sources/gems.move +++ b/examples/move/token/sources/gems.move @@ -75,8 +75,8 @@ module examples::gem { // rules for different types of actions. fun init(otw: GEM, ctx: &mut TxContext) { let (treasury_cap, coin_metadata) = coin::create_currency( - otw, 0, b"GEM", b"Capy Gems", // otw, decimal, symbol, name - b"In-game currency for Capy Miners", none(), // description, url + otw, 0, b"GEM", b"Gems", // otw, decimal, symbol, name + b"In-game currency for Miners", none(), // description, url ctx ); diff --git a/examples/trading/frontend/src/hooks/useTransactionExecution.ts b/examples/trading/frontend/src/hooks/useTransactionExecution.ts index 5218f9c7671..02a8b76aa7f 100644 --- a/examples/trading/frontend/src/hooks/useTransactionExecution.ts +++ b/examples/trading/frontend/src/hooks/useTransactionExecution.ts @@ -14,13 +14,13 @@ import toast from "react-hot-toast"; */ export function useTransactionExecution() { const client = useIotaClient(); - const { mutateAsync: signTransactionBlock } = useSignTransaction(); + const { mutateAsync: signTransaction } = useSignTransaction(); const executeTransaction = async ( txb: Transaction, ): Promise => { try { - const signature = await signTransactionBlock({ + const signature = await signTransaction({ transactionBlock: txb, }); diff --git a/sdk/dapp-kit/src/constants/walletDefaults.ts b/sdk/dapp-kit/src/constants/walletDefaults.ts index a043801736b..57ded2ec1e6 100644 --- a/sdk/dapp-kit/src/constants/walletDefaults.ts +++ b/sdk/dapp-kit/src/constants/walletDefaults.ts @@ -13,10 +13,7 @@ export const DEFAULT_STORAGE = export const DEFAULT_STORAGE_KEY = 'iota-dapp-kit:wallet-connection-info'; -const SIGN_FEATURES = [ - 'iota:signTransaction', - 'iota:signTransactionBlock', -] satisfies (keyof IotaWalletFeatures)[]; +const SIGN_FEATURES = ['iota:signTransaction'] satisfies (keyof IotaWalletFeatures)[]; export const DEFAULT_WALLET_FILTER = (wallet: WalletWithRequiredFeatures) => SIGN_FEATURES.some((feature) => wallet.features[feature]); diff --git a/sdk/dapp-kit/src/hooks/wallet/useSignAndExecuteTransaction.ts b/sdk/dapp-kit/src/hooks/wallet/useSignAndExecuteTransaction.ts index 2f5ce3c9433..0ac594c962c 100644 --- a/sdk/dapp-kit/src/hooks/wallet/useSignAndExecuteTransaction.ts +++ b/sdk/dapp-kit/src/hooks/wallet/useSignAndExecuteTransaction.ts @@ -122,10 +122,7 @@ export function useSignAndExecuteTransaction< } const chain = signTransactionArgs.chain ?? signerAccount?.chains[0]; - if ( - !currentWallet.features['iota:signTransaction'] && - !currentWallet.features['iota:signTransactionBlock'] - ) { + if (!currentWallet.features['iota:signTransaction']) { throw new WalletFeatureNotSupportedError( "This wallet doesn't support the `signTransaction` feature.", ); diff --git a/sdk/dapp-kit/src/hooks/wallet/useSignPersonalMessage.ts b/sdk/dapp-kit/src/hooks/wallet/useSignPersonalMessage.ts index da25a6f4bb8..f50a3b2e0c1 100644 --- a/sdk/dapp-kit/src/hooks/wallet/useSignPersonalMessage.ts +++ b/sdk/dapp-kit/src/hooks/wallet/useSignPersonalMessage.ts @@ -60,6 +60,13 @@ export function useSignPersonalMessage({ throw new WalletNotConnectedError('No wallet is connected.'); } + const signPersonalMessageFeature = currentWallet.features['iota:signPersonalMessage']; + if (!signPersonalMessageFeature) { + throw new WalletFeatureNotSupportedError( + "This wallet doesn't support the `signPersonalMessage` feature.", + ); + } + const signerAccount = signPersonalMessageArgs.account ?? currentAccount; if (!signerAccount) { throw new WalletNoAccountSelectedError( @@ -67,31 +74,10 @@ export function useSignPersonalMessage({ ); } - const signPersonalMessageFeature = currentWallet.features['iota:signPersonalMessage']; - if (signPersonalMessageFeature) { - return await signPersonalMessageFeature.signPersonalMessage({ - ...signPersonalMessageArgs, - account: signerAccount, - }); - } - - // TODO: Remove this once we officially discontinue iota:signMessage in the wallet standard - const signMessageFeature = currentWallet.features['iota:signMessage']; - if (signMessageFeature) { - console.warn( - "This wallet doesn't support the `signPersonalMessage` feature... falling back to `signMessage`.", - ); - - const { messageBytes, signature } = await signMessageFeature.signMessage({ - ...signPersonalMessageArgs, - account: signerAccount, - }); - return { bytes: messageBytes, signature }; - } - - throw new WalletFeatureNotSupportedError( - "This wallet doesn't support the `signPersonalMessage` feature.", - ); + return await signPersonalMessageFeature.signPersonalMessage({ + ...signPersonalMessageArgs, + account: signerAccount, + }); }, ...mutationOptions, }); diff --git a/sdk/dapp-kit/src/hooks/wallet/useSignTransaction.ts b/sdk/dapp-kit/src/hooks/wallet/useSignTransaction.ts index d09a781ee01..ffb53ed150e 100644 --- a/sdk/dapp-kit/src/hooks/wallet/useSignTransaction.ts +++ b/sdk/dapp-kit/src/hooks/wallet/useSignTransaction.ts @@ -78,10 +78,7 @@ export function useSignTransaction({ ); } - if ( - !currentWallet.features['iota:signTransaction'] && - !currentWallet.features['iota:signTransactionBlock'] - ) { + if (!currentWallet.features['iota:signTransaction']) { throw new WalletFeatureNotSupportedError( "This wallet doesn't support the `signTransaction` feature.", ); diff --git a/sdk/dapp-kit/src/hooks/wallet/useUnsafeBurnerWallet.ts b/sdk/dapp-kit/src/hooks/wallet/useUnsafeBurnerWallet.ts index 946eff3fd5d..fd464c63a2a 100644 --- a/sdk/dapp-kit/src/hooks/wallet/useUnsafeBurnerWallet.ts +++ b/sdk/dapp-kit/src/hooks/wallet/useUnsafeBurnerWallet.ts @@ -12,10 +12,8 @@ import type { StandardEventsFeature, StandardEventsOnMethod, IotaFeatures, - IotaSignAndExecuteTransactionBlockMethod, IotaSignAndExecuteTransactionMethod, IotaSignPersonalMessageMethod, - IotaSignTransactionBlockMethod, IotaSignTransactionMethod, Wallet, } from '@iota/wallet-standard'; @@ -58,12 +56,7 @@ function registerUnsafeBurnerWallet(iotaClient: IotaClient) { address: keypair.getPublicKey().toIotaAddress(), publicKey: keypair.getPublicKey().toIotaBytes(), chains: ['iota:unknown'], - features: [ - 'iota:signAndExecuteTransactionBlock', - 'iota:signTransactionBlock', - 'iota:signTransaction', - 'iota:signAndExecuteTransaction', - ], + features: ['iota:signTransaction', 'iota:signAndExecuteTransaction'], }); class UnsafeBurnerWallet implements Wallet { @@ -102,14 +95,6 @@ function registerUnsafeBurnerWallet(iotaClient: IotaClient) { version: '1.0.0', signPersonalMessage: this.#signPersonalMessage, }, - 'iota:signTransactionBlock': { - version: '1.0.0', - signTransactionBlock: this.#signTransactionBlock, - }, - 'iota:signAndExecuteTransactionBlock': { - version: '1.0.0', - signAndExecuteTransactionBlock: this.#signAndExecuteTransactionBlock, - }, 'iota:signTransaction': { version: '2.0.0', signTransaction: this.#signTransaction, @@ -134,18 +119,6 @@ function registerUnsafeBurnerWallet(iotaClient: IotaClient) { return { bytes, signature }; }; - #signTransactionBlock: IotaSignTransactionBlockMethod = async (transactionInput) => { - const { bytes, signature } = await transactionInput.transactionBlock.sign({ - client: iotaClient, - signer: keypair, - }); - - return { - transactionBlockBytes: bytes, - signature: signature, - }; - }; - #signTransaction: IotaSignTransactionMethod = async (transactionInput) => { const { bytes, signature } = await Transaction.from( await transactionInput.transaction.toJSON(), @@ -162,21 +135,6 @@ function registerUnsafeBurnerWallet(iotaClient: IotaClient) { }; }; - #signAndExecuteTransactionBlock: IotaSignAndExecuteTransactionBlockMethod = async ( - transactionInput, - ) => { - const { bytes, signature } = await transactionInput.transactionBlock.sign({ - client: iotaClient, - signer: keypair, - }); - - return iotaClient.executeTransactionBlock({ - signature, - transactionBlock: bytes, - options: transactionInput.options, - }); - }; - #signAndExecuteTransaction: IotaSignAndExecuteTransactionMethod = async ( transactionInput, ) => { diff --git a/sdk/dapp-kit/test/hooks/useSignPersonalMessage.test.tsx b/sdk/dapp-kit/test/hooks/useSignPersonalMessage.test.tsx index f00fdb4dbea..765a3e5ad88 100644 --- a/sdk/dapp-kit/test/hooks/useSignPersonalMessage.test.tsx +++ b/sdk/dapp-kit/test/hooks/useSignPersonalMessage.test.tsx @@ -10,7 +10,7 @@ import { WalletNotConnectedError, } from '../../src/errors/walletErrors.js'; import { useConnectWallet, useSignPersonalMessage } from '../../src/index.js'; -import { signMessageFeature, iotaFeatures } from '../mocks/mockFeatures.js'; +import { iotaFeatures } from '../mocks/mockFeatures.js'; import { createWalletProviderContextWrapper, registerMockWallet } from '../test-utils.js'; describe('useSignPersonalMessage', () => { @@ -50,42 +50,6 @@ describe('useSignPersonalMessage', () => { act(() => unregister()); }); - test('falls back to the `iota:signMessage` feature with a wallet that lacks support for `iota:signPersonalMessage`.', async () => { - const { unregister, mockWallet } = registerMockWallet({ - walletName: 'Mock Wallet 1', - features: signMessageFeature, - }); - - const wrapper = createWalletProviderContextWrapper(); - const { result } = renderHook( - () => ({ - connectWallet: useConnectWallet(), - signPersonalMessage: useSignPersonalMessage(), - }), - { wrapper }, - ); - - result.current.connectWallet.mutate({ wallet: mockWallet }); - await waitFor(() => expect(result.current.connectWallet.isSuccess).toBe(true)); - - const mockSignMessageFeature = mockWallet.features['iota:signMessage']; - const signMessageMock = mockSignMessageFeature!.signMessage as Mock; - - signMessageMock.mockReturnValueOnce({ messageBytes: 'abc', signature: '123' }); - - result.current.signPersonalMessage.mutate({ - message: new Uint8Array().fill(123), - }); - - await waitFor(() => expect(result.current.signPersonalMessage.isSuccess).toBe(true)); - expect(result.current.signPersonalMessage.data).toStrictEqual({ - bytes: 'abc', - signature: '123', - }); - - act(() => unregister()); - }); - test('signing a personal message from the currently connected account works successfully', async () => { const { unregister, mockWallet } = registerMockWallet({ walletName: 'Mock Wallet 1', diff --git a/sdk/dapp-kit/test/mocks/mockAccount.ts b/sdk/dapp-kit/test/mocks/mockAccount.ts index 6989e3a8fff..8091c3fe244 100644 --- a/sdk/dapp-kit/test/mocks/mockAccount.ts +++ b/sdk/dapp-kit/test/mocks/mockAccount.ts @@ -12,12 +12,7 @@ export function createMockAccount(accountOverrides: Partial = {}) address: keypair.getPublicKey().toIotaAddress(), publicKey: keypair.getPublicKey().toIotaBytes(), chains: ['iota:unknown'], - features: [ - 'iota:signAndExecuteTransactionBlock', - 'iota:signTransactionBlock', - 'iota:signAndExecuteTransaction', - 'iota:signTransaction', - ], + features: ['iota:signAndExecuteTransaction', 'iota:signTransaction'], ...accountOverrides, }); } diff --git a/sdk/dapp-kit/test/mocks/mockFeatures.ts b/sdk/dapp-kit/test/mocks/mockFeatures.ts index 6059d3bf8f4..f65aeebc6e8 100644 --- a/sdk/dapp-kit/test/mocks/mockFeatures.ts +++ b/sdk/dapp-kit/test/mocks/mockFeatures.ts @@ -2,14 +2,7 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import type { IdentifierRecord, IotaFeatures, IotaSignMessageFeature } from '@iota/wallet-standard'; - -export const signMessageFeature: IotaSignMessageFeature = { - 'iota:signMessage': { - version: '1.0.0', - signMessage: vi.fn(), - }, -}; +import type { IdentifierRecord, IotaFeatures } from '@iota/wallet-standard'; export const superCoolFeature: IdentifierRecord = { 'my-dapp:super-cool-feature': { @@ -19,23 +12,14 @@ export const superCoolFeature: IdentifierRecord = { }; export const iotaFeatures: IotaFeatures = { - ...signMessageFeature, 'iota:signPersonalMessage': { version: '1.0.0', signPersonalMessage: vi.fn(), }, - 'iota:signTransactionBlock': { - version: '1.0.0', - signTransactionBlock: vi.fn(), - }, 'iota:signTransaction': { version: '2.0.0', signTransaction: vi.fn(), }, - 'iota:signAndExecuteTransactionBlock': { - version: '1.0.0', - signAndExecuteTransactionBlock: vi.fn(), - }, 'iota:signAndExecuteTransaction': { version: '2.0.0', signAndExecuteTransaction: vi.fn(), diff --git a/sdk/graphql-transport/src/methods.ts b/sdk/graphql-transport/src/methods.ts index 60e39b15542..4ec6566bdad 100644 --- a/sdk/graphql-transport/src/methods.ts +++ b/sdk/graphql-transport/src/methods.ts @@ -1069,8 +1069,7 @@ export const RPC_METHODS: { }, }; }, - async executeTransactionBlock(transport, [txBytes, signatures, options, _requestType]) { - // TODO: requestType + async executeTransactionBlock(transport, [txBytes, signatures, options]) { const { effects, errors } = await transport.graphqlQuery( { query: ExecuteTransactionBlockDocument, diff --git a/sdk/kiosk/test/e2e/setup.ts b/sdk/kiosk/test/e2e/setup.ts index 4271feaf61e..37b4dfead95 100644 --- a/sdk/kiosk/test/e2e/setup.ts +++ b/sdk/kiosk/test/e2e/setup.ts @@ -235,8 +235,12 @@ export async function executeTransaction( showEvents: true, showObjectChanges: true, }, - requestType: 'WaitForLocalExecution', }); + + await toolbox.client.waitForTransaction({ + digest: resp.digest, + }); + expect(resp.effects?.status.status).toEqual('success'); return resp; } diff --git a/sdk/typescript/README.md b/sdk/typescript/README.md index e0c0ff6b387..9aae6026a27 100644 --- a/sdk/typescript/README.md +++ b/sdk/typescript/README.md @@ -179,7 +179,7 @@ For a primer for building transactions, refer to ```typescript import { getFullnodeUrl, IotaClient } from '@iota/iota-sdk/client'; import { Ed25519Keypair } from '@iota/iota-sdk/keypairs/ed25519'; -import { TransactionBlock } from '@iota/iota-sdk/transactions'; +import { Transaction } from '@iota/iota-sdk/transactions'; // Generate a new Ed25519 Keypair const keypair = new Ed25519Keypair(); @@ -187,14 +187,14 @@ const client = new IotaClient({ url: getFullnodeUrl('testnet'), }); -const tx = new TransactionBlock(); +const tx = new Transaction(); tx.transferObjects( ['0xe19739da1a701eadc21683c5b127e62b553e833e8a15a4f292f4f48b4afea3f2'], '0x1d20dcdb2bca4f508ea9613994683eb4e76e9c4ed371169677c1be02aaf0b12a', ); -const result = await client.signAndExecuteTransactionBlock({ +const result = await client.signAndExecuteTransaction({ signer: keypair, - transactionBlock: tx, + transaction: tx, }); console.log({ result }); ``` @@ -206,7 +206,7 @@ To transfer `1000` NANOS to another address: ```typescript import { getFullnodeUrl, IotaClient } from '@iota/iota-sdk/client'; import { Ed25519Keypair } from '@iota/iota-sdk/keypairs/ed25519'; -import { TransactionBlock } from '@iota/iota-sdk/transactions'; +import { Transaction } from '@iota/iota-sdk/transactions'; // Generate a new Ed25519 Keypair const keypair = new Ed25519Keypair(); @@ -214,12 +214,12 @@ const client = new IotaClient({ url: getFullnodeUrl('testnet'), }); -const tx = new TransactionBlock(); +const tx = new Transaction(); const [coin] = tx.splitCoins(tx.gas, [1000]); tx.transferObjects([coin], keypair.getPublicKey().toIotaAddress()); -const result = await client.signAndExecuteTransactionBlock({ +const result = await client.signAndExecuteTransaction({ signer: keypair, - transactionBlock: tx, + transaction: tx, }); console.log({ result }); ``` @@ -229,7 +229,7 @@ console.log({ result }); ```typescript import { getFullnodeUrl, IotaClient } from '@iota/iota-sdk/client'; import { Ed25519Keypair } from '@iota/iota-sdk/keypairs/ed25519'; -import { TransactionBlock } from '@iota/iota-sdk/transactions'; +import { Transaction } from '@iota/iota-sdk/transactions'; // Generate a new Ed25519 Keypair const keypair = new Ed25519Keypair(); @@ -237,13 +237,13 @@ const client = new IotaClient({ url: getFullnodeUrl('testnet'), }); -const tx = new TransactionBlock(); +const tx = new Transaction(); tx.mergeCoins('0xe19739da1a701eadc21683c5b127e62b553e833e8a15a4f292f4f48b4afea3f2', [ '0x127a8975134a4824d9288722c4ee4fc824cd22502ab4ad9f6617f3ba19229c1b', ]); -const result = await client.signAndExecuteTransactionBlock({ +const result = await client.signAndExecuteTransaction({ signer: keypair, - transactionBlock: tx, + transaction: tx, }); console.log({ result }); ``` @@ -253,7 +253,7 @@ console.log({ result }); ```typescript import { getFullnodeUrl, IotaClient } from '@iota/iota-sdk/client'; import { Ed25519Keypair } from '@iota/iota-sdk/keypairs/ed25519'; -import { TransactionBlock } from '@iota/iota-sdk/transactions'; +import { Transaction } from '@iota/iota-sdk/transactions'; // Generate a new Ed25519 Keypair const keypair = new Ed25519Keypair(); @@ -261,14 +261,14 @@ const client = new IotaClient({ url: getFullnodeUrl('testnet'), }); const packageObjectId = '0x...'; -const tx = new TransactionBlock(); +const tx = new Transaction(); tx.moveCall({ target: `${packageObjectId}::nft::mint`, arguments: [tx.pure.string('Example NFT')], }); -const result = await client.signAndExecuteTransactionBlock({ +const result = await client.signAndExecuteTransaction({ signer: keypair, - transactionBlock: tx, + transaction: tx, }); console.log({ result }); ``` @@ -280,7 +280,7 @@ To publish a package: ```typescript import { getFullnodeUrl, IotaClient } from '@iota/iota-sdk/client'; import { Ed25519Keypair } from '@iota/iota-sdk/keypairs/ed25519'; -import { TransactionBlock } from '@iota/iota-sdk/transactions'; +import { Transaction } from '@iota/iota-sdk/transactions'; const { execSync } = require('child_process'); // Generate a new Ed25519 Keypair @@ -293,15 +293,15 @@ const { modules, dependencies } = JSON.parse( encoding: 'utf-8', }), ); -const tx = new TransactionBlock(); +const tx = new Transaction(); const [upgradeCap] = tx.publish({ modules, dependencies, }); tx.transferObjects([upgradeCap], await client.getAddress()); -const result = await client.signAndExecuteTransactionBlock({ +const result = await client.signAndExecuteTransaction({ signer: keypair, - transactionBlock: tx, + transaction: tx, }); console.log({ result }); ``` @@ -361,7 +361,7 @@ import { getFullnodeUrl, IotaClient } from '@iota/iota-sdk/client'; const client = new IotaClient({ url: getFullnodeUrl('testnet'), }); -const txn = await client.getTransactionBlock({ +const txn = await client.getTransaction({ digest: '9XFneskU8tW7UxQf7tE5qFRfcN4FadtC2Z3HAZkgeETd=', // only fetch the effects field options: { @@ -374,7 +374,7 @@ const txn = await client.getTransactionBlock({ }); // You can also fetch multiple transactions in one batch request -const txns = await client.multiGetTransactionBlocks({ +const txns = await client.multiGetTransactions({ digests: [ '9XFneskU8tW7UxQf7tE5qFRfcN4FadtC2Z3HAZkgeETd=', '17mn5W1CczLwitHCO9OIUbqirNrQ0cuKdyxaNe16SAME=', diff --git a/sdk/typescript/src/client/client.ts b/sdk/typescript/src/client/client.ts index ab709b764d2..e1fd627a2bc 100644 --- a/sdk/typescript/src/client/client.ts +++ b/sdk/typescript/src/client/client.ts @@ -413,7 +413,6 @@ export class IotaClient { transactionBlock, signature, options, - requestType, }: ExecuteTransactionBlockParams): Promise { const result: IotaTransactionBlockResponse = await this.transport.request({ method: 'iota_executeTransactionBlock', @@ -424,16 +423,6 @@ export class IotaClient { ], }); - if (requestType === 'WaitForLocalExecution') { - try { - await this.waitForTransaction({ - digest: result.digest, - }); - } catch (_) { - // Ignore error while waiting for transaction - } - } - return result; } diff --git a/sdk/typescript/test/e2e/read-transactions.test.ts b/sdk/typescript/test/e2e/read-transactions.test.ts index d7d91c19840..57489004ab8 100644 --- a/sdk/typescript/test/e2e/read-transactions.test.ts +++ b/sdk/typescript/test/e2e/read-transactions.test.ts @@ -35,7 +35,6 @@ describe('Transaction Reading API', () => { return toolbox.client.signAndExecuteTransaction({ signer: toolbox.keypair, transaction: tx, - requestType: 'WaitForEffectsCert', }); } diff --git a/sdk/typescript/test/e2e/utils/setup.ts b/sdk/typescript/test/e2e/utils/setup.ts index 6867ebb4d16..192aba48fc5 100644 --- a/sdk/typescript/test/e2e/utils/setup.ts +++ b/sdk/typescript/test/e2e/utils/setup.ts @@ -326,9 +326,16 @@ export async function payIota( showEffects: true, showObjectChanges: true, }, - requestType: 'WaitForLocalExecution', }); + try { + await client.waitForTransaction({ + digest: txn.digest, + }); + } catch (_) { + // Ignore error while waiting for transaction + } + expect(txn.effects?.status.status).toEqual('success'); return txn; } diff --git a/sdk/wallet-standard/src/features/index.ts b/sdk/wallet-standard/src/features/index.ts index ad97f8d891b..dcf4efdd6dd 100644 --- a/sdk/wallet-standard/src/features/index.ts +++ b/sdk/wallet-standard/src/features/index.ts @@ -12,22 +12,15 @@ import type { import type { IotaReportTransactionEffectsFeature } from './iotaReportTransactionEffects.js'; import type { IotaSignAndExecuteTransactionFeature } from './iotaSignAndExecuteTransaction.js'; -import type { IotaSignAndExecuteTransactionBlockFeature } from './iotaSignAndExecuteTransactionBlock.js'; -import type { IotaSignMessageFeature } from './iotaSignMessage.js'; import type { IotaSignPersonalMessageFeature } from './iotaSignPersonalMessage.js'; import type { IotaSignTransactionFeature } from './iotaSignTransaction.js'; -import type { IotaSignTransactionBlockFeature } from './iotaSignTransactionBlock.js'; /** * Wallet Standard features that are unique to IOTA, and that all IOTA wallets are expected to implement. */ -export type IotaFeatures = Partial & - Partial & - IotaSignPersonalMessageFeature & +export type IotaFeatures = IotaSignPersonalMessageFeature & IotaSignAndExecuteTransactionFeature & IotaSignTransactionFeature & - // This deprecated feature should be removed once wallets update to the new method: - Partial & Partial; export type IotaWalletFeatures = StandardConnectFeature & @@ -50,10 +43,7 @@ export type WalletWithRequiredFeatures = WalletWithFeatures< export type MinimallyRequiredFeatures = StandardConnectFeature & StandardEventsFeature; -export * from './iotaSignMessage.js'; -export * from './iotaSignTransactionBlock.js'; export * from './iotaSignTransaction.js'; -export * from './iotaSignAndExecuteTransactionBlock.js'; export * from './iotaSignAndExecuteTransaction.js'; export * from './iotaSignPersonalMessage.js'; export * from './iotaReportTransactionEffects.js'; diff --git a/sdk/wallet-standard/src/features/iotaSignAndExecuteTransaction.ts b/sdk/wallet-standard/src/features/iotaSignAndExecuteTransaction.ts index a2eb60b9d52..fd759c1ba4a 100644 --- a/sdk/wallet-standard/src/features/iotaSignAndExecuteTransaction.ts +++ b/sdk/wallet-standard/src/features/iotaSignAndExecuteTransaction.ts @@ -2,6 +2,7 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +import type { IotaTransactionBlockResponseOptions } from '@iota/iota-sdk/client'; import type { SignedTransaction, IotaSignTransactionInput } from './iotaSignTransaction.js'; /** The latest API version of the signAndExecuteTransactionBlock API. */ @@ -26,7 +27,10 @@ export type IotaSignAndExecuteTransactionMethod = ( ) => Promise; /** Input for signing and sending transactions. */ -export interface IotaSignAndExecuteTransactionInput extends IotaSignTransactionInput {} +export interface IotaSignAndExecuteTransactionInput extends IotaSignTransactionInput { + /** specify which fields to return (e.g., transaction, effects, events, etc). By default, only the transaction digest will be returned. */ + options?: IotaTransactionBlockResponseOptions; +} /** Output of signing and sending transactions. */ export interface IotaSignAndExecuteTransactionOutput extends SignedTransaction { diff --git a/sdk/wallet-standard/src/features/iotaSignAndExecuteTransactionBlock.ts b/sdk/wallet-standard/src/features/iotaSignAndExecuteTransactionBlock.ts deleted file mode 100644 index 5d25085c625..00000000000 --- a/sdk/wallet-standard/src/features/iotaSignAndExecuteTransactionBlock.ts +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import type { - ExecuteTransactionRequestType, - IotaTransactionBlockResponse, - IotaTransactionBlockResponseOptions, -} from '@iota/iota-sdk/client'; - -import type { IotaSignTransactionBlockInput } from './iotaSignTransactionBlock.js'; - -/** The latest API version of the signAndExecuteTransactionBlock API. */ -export type IotaSignAndExecuteTransactionBlockVersion = '1.0.0'; - -/** - * @deprecated Use `iota:signAndExecuteTransaction` instead. - * - * A Wallet Standard feature for signing a transaction, and submitting it to the - * network. The wallet is expected to submit the transaction to the network via RPC, - * and return the transaction response. - */ -export type IotaSignAndExecuteTransactionBlockFeature = { - /** Namespace for the feature. */ - 'iota:signAndExecuteTransactionBlock': { - /** Version of the feature API. */ - version: IotaSignAndExecuteTransactionBlockVersion; - /** @deprecated Use `iota:signAndExecuteTransaction` instead. */ - signAndExecuteTransactionBlock: IotaSignAndExecuteTransactionBlockMethod; - }; -}; - -/** @deprecated Use `iota:signAndExecuteTransaction` instead. */ -export type IotaSignAndExecuteTransactionBlockMethod = ( - input: IotaSignAndExecuteTransactionBlockInput, -) => Promise; - -/** Input for signing and sending transactions. */ -export interface IotaSignAndExecuteTransactionBlockInput extends IotaSignTransactionBlockInput { - /** - * @deprecated requestType will be ignored by JSON RPC in the future - */ - requestType?: ExecuteTransactionRequestType; - /** specify which fields to return (e.g., transaction, effects, events, etc). By default, only the transaction digest will be returned. */ - options?: IotaTransactionBlockResponseOptions; -} - -/** Output of signing and sending transactions. */ -export interface IotaSignAndExecuteTransactionBlockOutput extends IotaTransactionBlockResponse {} diff --git a/sdk/wallet-standard/src/features/iotaSignMessage.ts b/sdk/wallet-standard/src/features/iotaSignMessage.ts deleted file mode 100644 index 3689589e6cb..00000000000 --- a/sdk/wallet-standard/src/features/iotaSignMessage.ts +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import type { WalletAccount } from '@wallet-standard/core'; - -/** - * The latest API version of the signMessage API. - * @deprecated Wallets can still implement this method for compatibility, but this has been replaced by the `iota:signPersonalMessage` feature - */ -export type IotaSignMessageVersion = '1.0.0'; - -/** - * A Wallet Standard feature for signing a personal message, and returning the - * message bytes that were signed, and message signature. - * - * @deprecated Wallets can still implement this method for compatibility, but this has been replaced by the `iota:signPersonalMessage` feature - */ -export type IotaSignMessageFeature = { - /** Namespace for the feature. */ - 'iota:signMessage': { - /** Version of the feature API. */ - version: IotaSignMessageVersion; - signMessage: IotaSignMessageMethod; - }; -}; - -/** @deprecated Wallets can still implement this method for compatibility, but this has been replaced by the `iota:signPersonalMessage` feature */ -export type IotaSignMessageMethod = (input: IotaSignMessageInput) => Promise; - -/** - * Input for signing messages. - * @deprecated Wallets can still implement this method for compatibility, but this has been replaced by the `iota:signPersonalMessage` feature - */ -export interface IotaSignMessageInput { - message: Uint8Array; - account: WalletAccount; -} - -/** - * Output of signing messages. - * @deprecated Wallets can still implement this method for compatibility, but this has been replaced by the `iota:signPersonalMessage` feature - */ -export interface IotaSignMessageOutput { - /** Base64 message bytes. */ - messageBytes: string; - /** Base64 encoded signature */ - signature: string; -} diff --git a/sdk/wallet-standard/src/features/iotaSignTransaction.ts b/sdk/wallet-standard/src/features/iotaSignTransaction.ts index 7451cb778ca..d4dd0d4fef8 100644 --- a/sdk/wallet-standard/src/features/iotaSignTransaction.ts +++ b/sdk/wallet-standard/src/features/iotaSignTransaction.ts @@ -40,3 +40,5 @@ export interface SignedTransaction { /** Base64 encoded signature */ signature: string; } + +export interface IotaSignTransactionOutput extends SignedTransaction {} diff --git a/sdk/wallet-standard/src/features/iotaSignTransactionBlock.ts b/sdk/wallet-standard/src/features/iotaSignTransactionBlock.ts deleted file mode 100644 index 3deb8bebe9d..00000000000 --- a/sdk/wallet-standard/src/features/iotaSignTransactionBlock.ts +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import type { Transaction } from '@iota/iota-sdk/transactions'; -import type { IdentifierString, WalletAccount } from '@wallet-standard/core'; - -/** The latest API version of the signTransactionBlock API. */ -export type IotaSignTransactionBlockVersion = '1.0.0'; - -/** - * @deprecated Use `iota:signTransaction` instead. - * - * A Wallet Standard feature for signing a transaction, and returning the - * serialized transaction and transaction signature. - */ -export type IotaSignTransactionBlockFeature = { - /** Namespace for the feature. */ - 'iota:signTransactionBlock': { - /** Version of the feature API. */ - version: IotaSignTransactionBlockVersion; - /** @deprecated Use `iota:signTransaction` instead. */ - signTransactionBlock: IotaSignTransactionBlockMethod; - }; -}; - -/** @deprecated Use `iota:signTransaction` instead. */ -export type IotaSignTransactionBlockMethod = ( - input: IotaSignTransactionBlockInput, -) => Promise; - -/** Input for signing transactions. */ -export interface IotaSignTransactionBlockInput { - transactionBlock: Transaction; - account: WalletAccount; - chain: IdentifierString; -} - -/** Output of signing transactions. */ -export interface IotaSignTransactionBlockOutput extends SignedTransactionBlock {} - -export interface SignedTransactionBlock { - /** Transaction as base64 encoded bcs. */ - transactionBlockBytes: string; - /** Base64 encoded signature */ - signature: string; -} diff --git a/sdk/wallet-standard/src/wallet.ts b/sdk/wallet-standard/src/wallet.ts index 6176dff2c0c..d427387664c 100644 --- a/sdk/wallet-standard/src/wallet.ts +++ b/sdk/wallet-standard/src/wallet.ts @@ -2,9 +2,6 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { bcs } from '@iota/iota-sdk/bcs'; -import { Transaction } from '@iota/iota-sdk/transactions'; -import { fromB64, toB64 } from '@iota/iota-sdk/utils'; import type { WalletWithFeatures } from '@wallet-standard/core'; import type { @@ -34,69 +31,24 @@ export async function signAndExecuteTransaction( wallet: WalletWithFeatures>, input: IotaSignAndExecuteTransactionInput, ) { - if (wallet.features['iota:signAndExecuteTransaction']) { - return wallet.features['iota:signAndExecuteTransaction'].signAndExecuteTransaction(input); - } - - if (!wallet.features['iota:signAndExecuteTransactionBlock']) { + if (!wallet.features['iota:signAndExecuteTransaction']) { throw new Error( `Provided wallet (${wallet.name}) does not support the signAndExecuteTransaction feature.`, ); } - const { signAndExecuteTransactionBlock } = - wallet.features['iota:signAndExecuteTransactionBlock']; - - const transactionBlock = Transaction.from(await input.transaction.toJSON()); - const { digest, rawEffects, rawTransaction } = await signAndExecuteTransactionBlock({ - account: input.account, - chain: input.chain, - transactionBlock, - options: { - showRawEffects: true, - showRawInput: true, - }, - }); - - const [ - { - txSignatures: [signature], - intentMessage: { value: bcsTransaction }, - }, - ] = bcs.SenderSignedData.parse(fromB64(rawTransaction!)); - - const bytes = bcs.TransactionData.serialize(bcsTransaction).toBase64(); - - return { - digest, - signature, - bytes, - effects: toB64(new Uint8Array(rawEffects!)), - }; + return wallet.features['iota:signAndExecuteTransaction'].signAndExecuteTransaction(input); } export async function signTransaction( wallet: WalletWithFeatures>, input: IotaSignTransactionInput, ) { - if (wallet.features['iota:signTransaction']) { - return wallet.features['iota:signTransaction'].signTransaction(input); - } - - if (!wallet.features['iota:signTransactionBlock']) { + if (!wallet.features['iota:signTransaction']) { throw new Error( `Provided wallet (${wallet.name}) does not support the signTransaction feature.`, ); } - const { signTransactionBlock } = wallet.features['iota:signTransactionBlock']; - - const transaction = Transaction.from(await input.transaction.toJSON()); - const { transactionBlockBytes, signature } = await signTransactionBlock({ - transactionBlock: transaction, - account: input.account, - chain: input.chain, - }); - - return { bytes: transactionBlockBytes, signature }; + return wallet.features['iota:signTransaction'].signTransaction(input); }