Skip to content
This repository has been archived by the owner on Jun 21, 2023. It is now read-only.

Commit

Permalink
Merge pull request #450 from PureStake/jan/arc-0001
Browse files Browse the repository at this point in the history
ARC-0001 Updates
  • Loading branch information
PureBrent authored Oct 4, 2022
2 parents b38f9bf + fd8fd39 commit d3b63a9
Show file tree
Hide file tree
Showing 11 changed files with 589 additions and 276 deletions.
32 changes: 23 additions & 9 deletions docs/dApp-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -459,22 +459,36 @@ AlgoSigner.send({
- Custom networks beta support is now in AlgoSigner. [Setup Guide](add-network.md)
- AlgoSigner.accounts(ledger) has changed such that calls now accept names that have been added to the user's custom network list as valid ledger names.
- A non-matching ledger name will result in a error:
- [RequestError.UnsupportedLedger] The provided ledger is not supported.
- The provided ledger is not supported (Code: 4200).
- An empty request will result with an error:
- Ledger not provided. Please use a base ledger: [TestNet,MainNet] or an available custom one [{"name":"Theta","genesisId":"thetanet-v1.0"}].
- Transaction requests will require a valid matching "genesisId", even for custom networks.

## Rejection Messages
## Signature Rejection Messages

The dApp may return the following errors in case of users rejecting requests, or errors in the request:
AlgoSigner may return some of the following error codes when requesting signatures:

| Error Code | Description | Additional notes |
| ----------- | ----------- | --------------- |
| 4000 | An unknown error occured. | N/A |
| 4001 | The user rejected the signature request. | N/A |
| 4100 | The requested operation and/or account has not been authorized by the user. | This is usually due to the connection between the dApp and the wallet becoming stale and the user [needing to reconnect](connection-issues.md). Otherwise, it may signal that you are trying to sign with private keys not found on AlgoSigner. |
| 4200 | The wallet does not support the requested operation. | N/A |
| 4201 | The wallet does not support signing that many transactions at a time. | The max number of transactions per group is 16. For Ledger devices, they can't sign more than one transaction at the same time. |
| 4202 | The wallet was not initialized properly beforehand. | Users need to have imported or created an account on AlgoSigner before connecting to dApps |
| 4300 | The input provided is invalid. | AlgoSigner rejected some of the transactions due to invalid fields. |

Additional information, if available, would be provided in the `data` field of the error object.

Returned errors have the following object structure:

```
UserRejected = '[RequestError.UserRejected] The extension user does not authorize the request.',
NotAuthorized = '[RequestError.NotAuthorized] The extension user does not authorize the request.',
UnsupportedAlgod = '[RequestError.UnsupportedAlgod] The provided method is not supported.',
UnsupportedLedger = '[RequestError.UnsupportedLedger] The provided ledger is not supported.',
InvalidFormat = '[RequestError.InvalidFormat] Please provide an array of either valid transaction objects or nested arrays of valid transaction objects.',
Undefined = '[RequestError.Undefined] An undefined error occurred.',
{
message: string;
code: number;
name: string;
data?: any;
}
```

Errors may be passed back to the dApp from the Algorand JS SDK if a transaction is valid, but has some other issue - for example, insufficient funds in the sending account.
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/common.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ test('RequestError - Structure', () => {
const testError = RequestError.NoAccountMatch(address, ledger);

expect(testError).toMatchObject({
message: `No matching account found on AlgoSigner for address: "${address}" on network ${ledger}.`,
message: `No matching account found on AlgoSigner for address "${address}" on network ${ledger}.`,
code: 4100,
});

Expand Down
91 changes: 66 additions & 25 deletions packages/common/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,57 +9,84 @@ export class RequestError {

static None = new RequestError('', 0);
static Undefined = new RequestError(
'[RequestError.Undefined] An undefined error occurred.',
'An undefined error occurred.',
4000
);
static UserRejected = new RequestError(
'[RequestError.UserRejected] The extension user does not authorize the request.',
'The extension user does not authorize the request.',
4001
);
static NotAuthorizedByUser = new RequestError(
'[RequestError.NotAuthorized] The extension user does not authorize the request.',
static SiteNotAuthorizedByUser = new RequestError(
'The extension user has not authorized requests from this website.',
4100
);
static NoMnemonicAvailable = (address: string): RequestError => new RequestError(
`The user does not possess the required private key to sign with for address: "${address}".`,
4100
);
static NoAccountMatch = (address: string, ledger: string): RequestError =>
new RequestError(
`No matching account found on AlgoSigner for address: "${address}" on network ${ledger}.`,
`No matching account found on AlgoSigner for address "${address}" on network ${ledger}.`,
4100
);
static UnsupportedAlgod = new RequestError(
'[RequestError.UnsupportedAlgod] The provided method is not supported.',
4200
);
static UnsupportedLedger = new RequestError(
'[RequestError.UnsupportedLedger] The provided ledger is not supported.',
4200
);
static NotAuthorizedOnChain = new RequestError(
'The user does not possess the required private key to sign with this address.',
4200
);
static MultipleTxsRequireGroup = new RequestError(
'If signing multiple transactions, they need to belong to a same group.',
'The provided ledger is not supported.',
4200
);
static PendingTransaction = new RequestError('Another query processing', 4201);
static LedgerMultipleTransactions = new RequestError(
'Ledger hardware device signing not available for multiple transactions.',
'Ledger hardware device signing is only available for one transaction at a time.',
4201
);
static TooManyTransactions = new RequestError(
`The ledger does not support signing more than ${RequestError.MAX_GROUP_SIZE} transactions at a time.`,
`AlgoSigner does not support signing more than ${RequestError.MAX_GROUP_SIZE} transactions at a time.`,
4201
);
static InvalidFields = (data?: any) =>
static AlgoSignerNotInitialized = new RequestError(
'AlgoSigner was not initialized properly beforehand.',
4202
);
static InvalidFields = (data?: any): RequestError =>
new RequestError('Validation failed for transaction due to invalid properties.', 4300, data);
static InvalidTransactionStructure = (data?: any) =>
static InvalidTransactionStructure = (data?: any): RequestError =>
new RequestError('Validation failed for transaction due to invalid structure.', 4300, data);
static InvalidFormat = new RequestError(
'[RequestError.InvalidFormat] Please provide an array of either valid transaction objects or nested arrays of valid transaction objects.',
4300
);
static InvalidSigners = new RequestError(
'Signers array should only be provided for multisigs (at least one signer) or for reference-only transactions belonging to a group (empty array).',
static CantMatchMsigSigners = (info: string): RequestError =>
new RequestError(
`AlgoSigner does not currently possess one of the requested signers for this multisig transaction: ${info}.`,
4300,
);
static InvalidSignerAddress = (address: string): RequestError =>
new RequestError(`Signers array contains the invalid address "${address}"`, 4300);
static MsigSignersMismatch = new RequestError(
"The 'signers' array contains addresses that aren't subaccounts of the requested multisig account.",
4300
);
static NoMsigSingleSigner = new RequestError(
"When a single-address 'signers' is provided with no 'msig' alongside it, it must match either the transaction sender or (if provided) the 'authAddr'",
4300
);
static NoMsigMultipleSigners = new RequestError(
"Multiple (1+) 'signers' should only be used alongside 'msig' transactions to specify which subaccounts to sign with.",
4300
);
static InvalidSignedTxn = new RequestError(
"The signed transaction provided ('{ stxn:... }') could not be parsed.",
4300
);
static NonMatchingSignedTxn = new RequestError(
"The signed transaction provided ('{ stxn:... }') doesn't match the corresponding unsigned transaction ('{ txn:... }').",
4300
);
static SignedTxnWithSigners = new RequestError(
"A signed transaction ('{ stxn:... }') must only be provided for reference transactions ('{ signers: [] }').",
4300
);
static NoTxsToSign = new RequestError(
"There are no transactions to sign as the provided ones are for reference-only ('{ signers: [] }').",
4300
);
static InvalidStructure = new RequestError(
Expand All @@ -70,10 +97,24 @@ export class RequestError {
"The provided multisig data doesn't adhere to the correct structure.",
4300
);
static InvalidMsigValues = (reason: string): RequestError =>
new RequestError(`The provided multisig data has invalid values: ${reason}.`, 4300);
static InvalidMsigAddress = (address: string): RequestError =>
new RequestError(`The address '${address}' is invalid.`, 4300);
static MsigAuthAddrMismatch = new RequestError(
"The provided 'authAddr' doesn't match the requested multisig account.",
4300
);
static InvalidAuthAddress = (address: string): RequestError =>
new RequestError(`'authAddr' contains the invalid address "${address}"`, 4300);
static IncompleteOrDisorderedGroup = new RequestError(
'The transaction group is incomplete or presented in a different order than when it was created.',
4300
);
static MultipleTxsRequireGroup = new RequestError(
'If signing multiple transactions, they need to belong to a same group.',
4300
);
static NonMatchingGroup = new RequestError(
'All transactions need to belong to the same group.',
4300
Expand All @@ -82,7 +123,7 @@ export class RequestError {
'All transactions need to belong to the same ledger.',
4300
);
static SigningError = (code: number, data?: any) =>
static SigningError = (code: number, data?: any): RequestError =>
new RequestError('There was a problem signing the transaction(s).', code, data);

protected constructor(message: string, code: number, data?: any) {
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export type WalletMultisigMetadata = {
export type WalletTransaction = {
readonly txn: string;
readonly signers?: Array<string>;
readonly stxn?: string;
readonly message?: string;
readonly msig?: WalletMultisigMetadata;
readonly authAddr?: string;
Expand Down
6 changes: 3 additions & 3 deletions packages/extension/src/background/messaging/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class OnMessageHandler extends RequestValidation {
try {
request.origin = new URL(sender.url).origin;
} catch (e) {
request.error = RequestError.NotAuthorizedByUser;
request.error = RequestError.SiteNotAuthorizedByUser;
MessageApi.send(request);
return;
}
Expand Down Expand Up @@ -79,7 +79,7 @@ export class OnMessageHandler extends RequestValidation {
new encryptionWrap('').checkStorage((exist: boolean) => {
// Reject message if there's no wallet
if (!exist) {
request.error = RequestError.NotAuthorizedByUser;
request.error = RequestError.AlgoSignerNotInitialized;
MessageApi.send(request);
} else {
if (OnMessageHandler.isAuthorization(method) && OnMessageHandler.isPublic(method)) {
Expand All @@ -98,7 +98,7 @@ export class OnMessageHandler extends RequestValidation {
});
} else {
// Origin is not authorized
request.error = RequestError.NotAuthorizedByUser;
request.error = RequestError.SiteNotAuthorizedByUser;
MessageApi.send(request);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ export class InternalMethods {
});
}

// Checks if an address is a valid user account for a given ledger.
public static checkValidAccount(genesisID: string, address: string): void {
// Checks if an account for the given address exists on AlgoSigner for a given ledger.
public static checkAccountIsImported(genesisID: string, address: string): void {
const ledger: string = getLedgerFromGenesisId(genesisID);
let found = false;
for (let i = session.wallet[ledger].length - 1; i >= 0; i--) {
Expand Down Expand Up @@ -891,7 +891,7 @@ export class InternalMethods {
// Return to close connection
return true;
} else if (!account.mnemonic) {
sendResponse({ error: RequestError.NotAuthorizedOnChain.message });
sendResponse({ error: RequestError.NoMnemonicAvailable(account.address).message });
} else {
// We can use a modified popup to allow the normal flow, but require extra scrutiny.
const recoveredAccount = algosdk.mnemonicToSecretKey(account.mnemonic);
Expand Down
Loading

0 comments on commit d3b63a9

Please sign in to comment.