Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add signTransaction() and signDelegateAction() to BaseWalletBehaviour interface #1213

Open
wants to merge 17 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,8 @@ export class WalletModules {
const _signIn = wallet.signIn;
const _signOut = wallet.signOut;
const _signMessage = wallet.signMessage;
const _signTransaction = wallet.signTransaction;
const _signDelegateAction = wallet.signDelegateAction;

wallet.signIn = async (params: never) => {
const accounts = await _signIn(params);
Expand Down Expand Up @@ -332,6 +334,42 @@ export class WalletModules {
return await _signMessage(params);
};

wallet.signTransaction = async (params: never) => {
if (_signTransaction === undefined) {
throw new Error(
`The signTransaction method is not supported by ${wallet.metadata.name}`
);
}

try {
return await _signTransaction(params);
} catch (error) {
throw new Error(
`Failed to sign transaction: ${
error instanceof Error ? error.message : String(error)
}`
);
}
};

wallet.signDelegateAction = async (params: never) => {
if (_signDelegateAction === undefined) {
throw new Error(
`The signDelegateAction method is not supported by ${wallet.metadata.name}`
);
}

try {
return await _signDelegateAction(params);
} catch (error) {
throw new Error(
`Failed to sign delegate action: ${
error instanceof Error ? error.message : String(error)
}`
);
}
};

return wallet;
}

Expand Down
126 changes: 126 additions & 0 deletions packages/core/src/lib/wallet/wallet.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ import type { ReadOnlyStore } from "../store.types";
import type { Transaction, Action } from "./transactions.types";
import type { Modify, Optional } from "../utils.types";
import type { FinalExecutionOutcome } from "near-api-js/lib/providers";
import type {
SignedTransaction,
Transaction as Tx,
Signature,
} from "near-api-js/lib/transaction";
import type { Signer } from "near-api-js/lib/signer";
import type { PublicKey } from "near-api-js/lib/utils";

interface BaseWalletMetadata {
/**
Expand Down Expand Up @@ -121,6 +128,112 @@ interface SignAndSendTransactionsParams {
transactions: Array<Optional<Transaction, "signerId">>;
}

interface SignTransactionParams {
/**
* The Transaction object to sign
*/
transaction: Tx;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

App developer in most of the cases cannot construct the full transaction since there is no public key and therefore there is no nonce. Also, it is often no account id either.

I cannot think of the use for SignTransactionParams interface as it is defined right now.

/**
* The {Signer} object that assists with signing keys
*/
signer: Signer;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you envision the use of it? Isn't the whole point to use an external wallet as signer? How is it required?

/**
* The human-readable NEAR account name
*/
accountId?: string;
/**
* The targeted network. (ex. default, betanet, etc…)
*/
networkId?: string;
}

interface SignTransactionActionsParams {
/**
* The NEAR account ID of the transaction receiver.
*/
receiverId: string;
/**
* Tx nonce.
*/
nonce: bigint;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tx nonce is attached to the public key nonce of the account, it does not make sense to require it from the app developer.

/**
* NEAR Action(s) to sign and send to the network (e.g. `FunctionCall`). You can find more information on `Action` {@link https://github.com/near/wallet-selector/blob/main/packages/core/docs/api/transactions.md | here}.
*/
actions: Array<Action>;
/**
* Block hash
*/
blockHash: Uint8Array;
/**
* The {Signer} object that assists with signing keys
*/
signer: Signer;
/**
* The human-readable NEAR account name
*/
accountId?: string;
/**
* The targeted network. (ex. default, betanet, etc…)
*/
networkId?: string;
}

interface MessageSigner {
sign(message: Uint8Array): Promise<Uint8Array>;
}

interface DelegateAction {
/**
* Account ID for the intended signer of the delegate action
*/
senderId: string;
Comment on lines +154 to +157
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

App developer does not know the senderId

/**
* The set of actions to be included in the meta transaction
*/
actions: Array<Action>;
/**
* Number of blocks past the current block height for which the SignedDelegate action may be included in a meta transaction
*/
blockHeightTtl: number;
/**
* Account ID for the intended receiver of the meta transaction
*/
receiverId: string;
/**
* Current nonce on the access key used to sign the delegate action
*/
nonce: bigint;
Comment on lines +166 to +169
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same problem as with transactions, see above

/**
* The maximum block height for which this action can be executed as part of a transaction
*/
maxBlockHeight: bigint;
/**
* Public key for the access key used to sign the delegate action
*/
publicKey: PublicKey;
Comment on lines +174 to +177
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

App developer does not have the user's public key

}

interface SignDelegateOptions {
/**
* Delegate action to be signed by the meta transaction sender
*/
delegateAction: DelegateAction;
/**
* Signer instance for the meta transaction sender
*/
signer: MessageSigner;
}

interface SignedDelegate {
delegateAction: DelegateAction;
signature: Signature;
}

interface SignedDelegateWithHash {
hash: Uint8Array;
signedDelegateAction: SignedDelegate;
}

interface BaseWalletBehaviour {
/**
* Programmatically sign in. Hardware wallets (e.g. Ledger) require `derivationPaths` to validate access key permissions.
Expand Down Expand Up @@ -155,6 +268,19 @@ interface BaseWalletBehaviour {
params: SignAndSendTransactionsParams
): Promise<Array<providers.FinalExecutionOutcome>>;
signMessage?(params: SignMessageParams): Promise<SignedMessage | void>;
/**
* Signs one or more NEAR Actions which can be broadcasted to the network.
* The user must be signed in to call this method.
*/
signTransaction?(
params: SignTransactionParams | SignTransactionActionsParams
): Promise<[Uint8Array, SignedTransaction]>;
/**
* Composes and signs a SignedDelegate action to be executed in a transaction
*/
signDelegateAction?(
params: SignDelegateOptions
): Promise<SignedDelegateWithHash>;
}

type BaseWallet<
Expand Down
Loading