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 #452 from PureStake/develop
Browse files Browse the repository at this point in the history
Patch 1.9.6
  • Loading branch information
PureBrent authored Oct 5, 2022
2 parents eb0a11b + b3b7d5b commit 4a45bf2
Show file tree
Hide file tree
Showing 21 changed files with 685 additions and 294 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Developers working with dApps may also install directly from the release package

An interactive transition guide is available [here](https://purestake.github.io/algosigner-dapp-example/v1v2TransitionGuide.html).

## 1.9.5 Release
## 1.9.6 Release

### Main updates
This update adds supports for easier transfers with the new autocomplete feature. Start typing an account, contact name or name service alias on the destination field when sending Algos or ASAs and you'll be able to select the desired address from a dropdown. This also marks the end of the support of the older signing methods that were previously available.
Expand All @@ -24,6 +24,7 @@ This update adds supports for easier transfers with the new autocomplete feature
- External name services (NFDomains and Algorand Namespace Service)
- `AlgoSigner.sign()` and `AlgoSigner.signMultisig()` have been deprecated
- New Account creation now occurs in the browser, improving ease of use when saving the mnemonic
- Improved dApp support with the new [`stxn`](docs/dApp-integration.md#providing-signed-reference-transactions) field, as well as new and more descriptive error types

### Other updates
- Improved Account Importing and Cache Clearing
Expand Down
109 changes: 92 additions & 17 deletions docs/dApp-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ Transactions objects need to be presented with the following structure:
{
txn: Base64-encoded string of a transaction binary,
signers?: [optional] array of addresses to sign with (defaults to the sender),
stxn?: [optional] Base64-encoded string of a signed transaction binary
multisig?: [optional] extra metadata needed for multisig transactions,
};
```
Expand Down Expand Up @@ -219,6 +220,7 @@ let signedTxs = await AlgoSigner.signTxn([
},
]);
```

Alternatively, you can provide multiple arrays of transactions at once. Same rules regarding the contents of the groups apply.

**Request**
Expand All @@ -237,6 +239,7 @@ AlgoSigner.signTxn([
],
]);
```

**Response**

```json
Expand Down Expand Up @@ -266,7 +269,7 @@ let binarySignedTx = AlgoSigner.encoding.base64ToMsgpack(signedTxs[0].blob);
await client.sendRawTransaction(binarySignedTx).do();
```

#### Atomic Transactions
### Atomic Transactions

For Atomic transactions, provide an array of transaction objects with the same group ID, _provided in the same order as when the group was assigned_.

Expand Down Expand Up @@ -309,11 +312,25 @@ let binarySignedTxs = signedTxs.map((tx) => AlgoSigner.encoding.base64ToMsgpack(
await client.sendRawTransaction(binarySignedTxs).do();
```

In case not all group transactions belong to accounts on AlgoSigner, you can set the `signers` field of the transaction object as an empty array to specify that it's only being sent to AlgoSigner for reference and group validation, not for signing.
#### Reference Atomic transactions

_AlgoSigner.signTxn()_ will return _null_ in it's response array for the positions were reference transactions were sent.
In case not all group transactions belong to accounts on AlgoSigner, you can set the `signers` field of the transaction object as an empty array to specify that it's only being sent to AlgoSigner for reference and group validation, not for signing. Reference transactions should look like this:

```js
{
txn: 'B64_TXN',
signers: [],
// This tells AlgoSigner that this transaction is not meant to be signed
}
```

In these cases, you'd have to sign the missing transaction by your own means before it can be sent (by using the SDK, for instance).
`AlgoSigner.signTxn()` will return `null` in the position(s) where reference transactions were provided. In these instances, you'd have to sign the missing transaction(s) by your own means before they can be sent. This is useful for transactions that require external signing, like `lsig` transactions.

#### Providing Signed reference transaction(s)

You can provide a signed reference transaction to AlgoSigner via the `stxn` field of the transaction object for it to be validated and returned as part of the group. For the transaction(s) where `stxn` was provided, `AlgoSigner.signTxn()` will return the `stxn` string in the same position of the response array as the corresponding reference transaction(s) instead of `null`.

**Example**

```js
let tx1 = new algosdk.Transaction({
Expand All @@ -338,17 +355,61 @@ let signedTxs = await AlgoSigner.signTxn([
txn: base64Txs[0],
},
{
// This tells AlgoSigner that this transaction is not meant to be signed
txn: base64Txs[1],
signers: [],
stxn: 'MANUALLY_SIGNED_SECOND_TXN_B64',
},
]);
```

**Response**

```js
[
{
txID: '...',
blob: 'ALGOSIGNER_SIGNED_B64',
},
{
txID: '...',
blob: 'MANUALLY_SIGNED_SECOND_TXN_B64',
},
];
```

#### Signing reference transactions manually

In case you can't or don't want to provide the `stxn`, the provided transaction should look like this:

**Example**

```js
let signedTxs = await AlgoSigner.signTxn([
{
txn: base64Txs[0],
},
{
txn: base64Txs[1],
signers: [],
},
]);
```

Signing the remaining transaction with the SDK would look like this:
**Response**

```js
[
{
txID: '...',
blob: 'ALGOSIGNER_SIGNED_B64',
},
null,
];
```

Afterwards, you can sign and send the remaining transaction(s) with the SDK:

```js
// The AlgoSigner.signTxn() response would look like '[{ txID, blob }, null]'
// Convert first transaction to binary from the response
let signedTx1Binary = AlgoSigner.encoding.base64ToMsgpack(signedTxs[0].blob);
// Sign leftover transaction with the SDK
Expand All @@ -358,7 +419,7 @@ let signedTx2Binary = tx2.signTxn(externalAccount.sk);
await client.sendRawTransaction([signedTx1Binary, signedTx2Binary]).do();
```

Alternatively, if you're using the [AlgoSigner.send()](#algosignersend-ledger-mainnettestnet-txblob-) to send the transaction, you have to merge the binaries before converting to a base64 encoded string.
Alternatively, if you're using the [AlgoSigner.send()](#algosignersend-ledger-mainnettestnet-txblob-) to send the transaction(s), you have to merge the binaries before converting to a single base64 encoded string.

```js
// Merge transaction binaries into a single Uint8Array
Expand Down Expand Up @@ -459,22 +520,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

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. |

The dApp may return the following errors in case of users rejecting requests, or errors in the request:
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 package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "algosigner",
"version": "1.9.5",
"version": "1.9.6",
"author": "https://developer.purestake.io",
"description": "Sign Algorand transactions in your browser with PureStake.",
"repository": "https://github.com/PureStake/algosigner",
Expand Down
2 changes: 1 addition & 1 deletion packages/common/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@algosigner/common",
"version": "1.9.5",
"version": "1.9.6",
"author": "https://developer.purestake.io",
"description": "Common library functions for AlgoSigner.",
"repository": "https://github.com/PureStake/algosigner",
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
2 changes: 1 addition & 1 deletion packages/crypto/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "algosigner-crypto",
"version": "1.9.5",
"version": "1.9.6",
"author": "https://developer.purestake.io",
"description": "Cryptographic wrapper for saving and retrieving extention information in AlgoSigner.",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/dapp/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@algosigner/dapp",
"version": "1.9.5",
"version": "1.9.6",
"author": "https://developer.purestake.io",
"repository": "https://github.com/PureStake/algosigner",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/extension/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "AlgoSigner",
"author": "https://developer.purestake.io",
"version": "1.9.5",
"version": "1.9.6",
"description": "Algorand Wallet Extension | Send & Receive ALGOs | Sign dApp Transactions",
"icons": {
"48": "icon.png"
Expand Down
Loading

0 comments on commit 4a45bf2

Please sign in to comment.