diff --git a/examples/wallet-api/package.json b/examples/wallet-api/package.json
index 204e7c469..3349b113f 100644
--- a/examples/wallet-api/package.json
+++ b/examples/wallet-api/package.json
@@ -9,7 +9,8 @@
"start:sign-message": "FILE=sign-message.html yarn start",
"start:sign-cis3-message": "FILE=sign-cis3-message.html yarn start",
"start:web3-id": "FILE=web3-id.html yarn start",
- "start:sign-token-update": "FILE=sign-token-update.html yarn start"
+ "start:sign-token-update": "FILE=sign-token-update.html yarn start",
+ "start:sponsored-transaction": "FILE=sponsored-transaction.html yarn start"
},
"dependencies": {
"@concordium/web-sdk": "12.0.0-devnet-p10.0"
diff --git a/examples/wallet-api/sign-token-update.html b/examples/wallet-api/sign-token-update.html
index 8b55d33b6..f2fecefe4 100644
--- a/examples/wallet-api/sign-token-update.html
+++ b/examples/wallet-api/sign-token-update.html
@@ -30,8 +30,15 @@
});
document.getElementById('signSingleTransferTokenUpdate').addEventListener('click', () => {
- const { TokenOperationType, Cbor, TokenAmount, TokenHolder, CborMemo, TokenId, AccountAddress } =
- concordiumSDK;
+ const {
+ TokenOperationType,
+ Cbor,
+ TokenAmount,
+ CborAccountAddress,
+ CborMemo,
+ TokenId,
+ AccountAddress,
+ } = concordiumSDK;
const ops = [
{
@@ -40,7 +47,7 @@
value: '1000000',
decimals: 6,
}),
- recipient: TokenHolder.fromAccountAddress(
+ recipient: CborAccountAddress.fromAccountAddress(
AccountAddress.fromBase58('4PVJxxtiNVanfuNgvFWtFT1JCiqoVs6npVXBqwLNKprqnUiC2A')
),
memo: CborMemo.fromString('Some text for memo'),
@@ -62,8 +69,15 @@
});
document.getElementById('signTokenUpdate').addEventListener('click', () => {
- const { TokenOperationType, Cbor, TokenAmount, TokenHolder, CborMemo, TokenId, AccountAddress } =
- concordiumSDK;
+ const {
+ TokenOperationType,
+ Cbor,
+ TokenAmount,
+ CborAccountAddress,
+ CborMemo,
+ TokenId,
+ AccountAddress,
+ } = concordiumSDK;
const ops = [
{
@@ -72,7 +86,7 @@
value: '1000000',
decimals: 6,
}),
- recipient: TokenHolder.fromAccountAddress(
+ recipient: CborAccountAddress.fromAccountAddress(
AccountAddress.fromBase58('4PVJxxtiNVanfuNgvFWtFT1JCiqoVs6npVXBqwLNKprqnUiC2A')
),
memo: CborMemo.fromString('Some text for memo'),
@@ -95,7 +109,7 @@
value: '200000',
decimals: 6,
}),
- recipient: TokenHolder.fromAccountAddress(
+ recipient: CborAccountAddress.fromAccountAddress(
AccountAddress.fromBase58('4PVJxxtiNVanfuNgvFWtFT1JCiqoVs6npVXBqwLNKprqnUiC2A')
),
memo: CborMemo.fromString('Some text for memo'),
diff --git a/examples/wallet-api/sponsored-transaction.html b/examples/wallet-api/sponsored-transaction.html
new file mode 100644
index 000000000..15cdd4a2a
--- /dev/null
+++ b/examples/wallet-api/sponsored-transaction.html
@@ -0,0 +1,162 @@
+
+
+
+ My cool dapp
+
+
+
+
+
+
+
+
+
+
+
Account address:
+
+
+
+
+ Sponsor Config:
+ Sponsor Account:
+
+
+ Sponsor Private Key:
+
+
+ Recipient Config:
+ Recipient Address:
+
+
+
+
+
+
+
diff --git a/packages/browser-wallet-api-helpers/src/wallet-api-types.ts b/packages/browser-wallet-api-helpers/src/wallet-api-types.ts
index eba8f2998..65923eb9a 100644
--- a/packages/browser-wallet-api-helpers/src/wallet-api-types.ts
+++ b/packages/browser-wallet-api-helpers/src/wallet-api-types.ts
@@ -25,6 +25,7 @@ import type {
UpdateContractInput,
UpdateCredentialsInput,
VerifiablePresentation,
+ Transaction,
} from '@concordium/web-sdk';
import type { RpcTransport } from '@protobuf-ts/runtime-rpc';
import { LaxNumberEnumValue, LaxStringEnumValue } from './util';
@@ -69,6 +70,8 @@ export type SendTransactionPayload =
| SendTransactionUpdateContractPayload
| SendTransactionInitContractPayload;
+export type SignableTransaction = Transaction.Signable;
+
export type SmartContractParameters =
| { [key: string]: SmartContractParameters }
| SmartContractParameters[]
@@ -254,6 +257,14 @@ interface MainWalletApi {
type: LaxNumberEnumValue,
payload: ConfigureDelegationPayload
): Promise;
+ /**
+ * Sends a transaction signed by sponsor to the Concordium Wallet and awaits the users action.
+ * Note that a header is sent, and constructed by the sponsor.
+ * Note that if the user rejects signing the transaction, this will throw an error.
+ * @param accountAddress the address of the account that should sign the transaction
+ * @param transaction the sponsored transaction with header to be signed and sent.
+ */
+ sendSponsoredTransaction(accountAddress: AccountAddressSource, transaction: SignableTransaction): Promise;
/**
* Sends a message to the Concordium Wallet and awaits the users action. If the user signs the message, this will resolve to the signature.
* Note that if the user rejects signing the message, this will throw an error.
diff --git a/packages/browser-wallet/CHANGELOG.md b/packages/browser-wallet/CHANGELOG.md
index 59f933074..d5d292112 100644
--- a/packages/browser-wallet/CHANGELOG.md
+++ b/packages/browser-wallet/CHANGELOG.md
@@ -2,6 +2,10 @@
## Unreleased
+## 2.7.2
+
+- Added support for Sponsored Transactions handling. With new method `sendSponsoredTransaction` in wallet-api.
+
## 2.7.0
### Changed
diff --git a/packages/browser-wallet/package.json b/packages/browser-wallet/package.json
index e27ba6bba..41e2cad34 100644
--- a/packages/browser-wallet/package.json
+++ b/packages/browser-wallet/package.json
@@ -1,7 +1,7 @@
{
"name": "@concordium/browser-wallet",
"private": true,
- "version": "2.7.0",
+ "version": "2.7.2",
"description": "Browser extension wallet for the Concordium blockchain",
"author": "Concordium Software",
"license": "Apache-2.0",
diff --git a/packages/browser-wallet/src/background/index.ts b/packages/browser-wallet/src/background/index.ts
index d1d58b635..452d3f16d 100644
--- a/packages/browser-wallet/src/background/index.ts
+++ b/packages/browser-wallet/src/background/index.ts
@@ -531,6 +531,14 @@ forwardToPopup(
undefined,
withPromptEnd
);
+forwardToPopup(
+ MessageType.SendSponsoredTransaction,
+ InternalMessageType.SendSponsoredTransaction,
+ runConditionComposer(runIfAccountIsAllowlisted, async () => ({ run: true }), withPromptStart()),
+ appendUrlToPayload,
+ undefined,
+ withPromptEnd
+);
forwardToPopup(
MessageType.SignMessage,
InternalMessageType.SignMessage,
diff --git a/packages/browser-wallet/src/messaging/message.ts b/packages/browser-wallet/src/messaging/message.ts
index b06f5d18d..f94e303e5 100644
--- a/packages/browser-wallet/src/messaging/message.ts
+++ b/packages/browser-wallet/src/messaging/message.ts
@@ -7,6 +7,7 @@ import { v4 as uuidv4 } from 'uuid';
*/
export enum MessageType {
SendTransaction = 'M_SendTransaction',
+ SendSponsoredTransaction = 'M_SendSponsoredTransaction',
SignMessage = 'M_SignMessage',
GetAccounts = 'M_GetAccounts',
GetSelectedAccount = 'M_GetSelectedAccount',
@@ -29,6 +30,7 @@ export enum InternalMessageType {
Init = 'I_Init',
PopupReady = 'I_PopupReady',
SendTransaction = 'I_SendTransaction',
+ SendSponsoredTransaction = 'I_SendSponsoredTransaction',
SignMessage = 'I_SignMessage',
Connect = 'I_Connect',
TestPopupOpen = 'I_TestPopupOpen',
diff --git a/packages/browser-wallet/src/popup/constants/routes.ts b/packages/browser-wallet/src/popup/constants/routes.ts
index 2a52f5335..47cdaf4aa 100644
--- a/packages/browser-wallet/src/popup/constants/routes.ts
+++ b/packages/browser-wallet/src/popup/constants/routes.ts
@@ -6,6 +6,8 @@ type RouteChildren = {
[key: string]: RouteNode | RoutePath;
};
+// ToDo sendSponsoredTransaction not implemented in OldUI
+// Need redirect to new UI, or Sunset OldUI
export const relativeRoutes = {
home: {
path: '/',
@@ -63,6 +65,9 @@ export const relativeRoutes = {
sendTransaction: {
path: 'send-transaction',
},
+ sendSponsoredTransaction: {
+ path: 'send-sponsored-transaction',
+ },
endIdentityIssuance: {
path: 'end-identity-issuance',
},
diff --git a/packages/browser-wallet/src/popup/popupX/constants/routes.ts b/packages/browser-wallet/src/popup/popupX/constants/routes.ts
index 6d55e07f6..b1f5b37b7 100644
--- a/packages/browser-wallet/src/popup/popupX/constants/routes.ts
+++ b/packages/browser-wallet/src/popup/popupX/constants/routes.ts
@@ -410,6 +410,9 @@ export const relativeRoutes = {
sendTransaction: {
path: 'sendTransaction',
},
+ sendSponsoredTransaction: {
+ path: 'sendSponsoredTransaction',
+ },
addWeb3IdCredential: {
path: 'add-web3id-credential',
},
diff --git a/packages/browser-wallet/src/popup/popupX/pages/SendFunds/Confirm.tsx b/packages/browser-wallet/src/popup/popupX/pages/SendFunds/Confirm.tsx
index cde31dccd..6d14ebd05 100644
--- a/packages/browser-wallet/src/popup/popupX/pages/SendFunds/Confirm.tsx
+++ b/packages/browser-wallet/src/popup/popupX/pages/SendFunds/Confirm.tsx
@@ -1,6 +1,6 @@
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
-import { Cbor, CborMemo, TokenAmount, TokenId, TokenOperationType, TokenHolder } from '@concordium/web-sdk/plt';
+import { Cbor, CborMemo, TokenAmount, TokenId, TokenOperationType, CborAccountAddress } from '@concordium/web-sdk/plt';
import {
AccountAddress,
AccountTransactionType,
@@ -116,7 +116,7 @@ export default function SendFundsConfirm({ values, fee, sender }: Props) {
value: parseTokenAmount(values.amount, tokenMetadata?.decimals).toString(),
decimals: tokenMetadata?.decimals || 0,
}),
- recipient: TokenHolder.fromAccountAddress(receiver),
+ recipient: CborAccountAddress.fromAccountAddress(receiver),
memo: values.memo ? CborMemo.fromString(values.memo) : undefined,
},
},
diff --git a/packages/browser-wallet/src/popup/popupX/pages/SendFunds/SendFunds.tsx b/packages/browser-wallet/src/popup/popupX/pages/SendFunds/SendFunds.tsx
index 9b4906454..8faa3e46c 100644
--- a/packages/browser-wallet/src/popup/popupX/pages/SendFunds/SendFunds.tsx
+++ b/packages/browser-wallet/src/popup/popupX/pages/SendFunds/SendFunds.tsx
@@ -18,7 +18,7 @@ import {
TokenId,
TokenAmount as TokenAmountPlt,
TokenOperationType,
- TokenHolder,
+ CborAccountAddress,
} from '@concordium/web-sdk/plt';
import { useAsyncMemo } from 'wallet-common-helpers';
import { useAtomValue } from 'jotai';
@@ -104,7 +104,7 @@ function SendFunds({ address }: SendFundsProps) {
value: parseTokenAmount(amount, metadata?.decimals).toString(),
decimals: metadata?.decimals || 0,
}),
- recipient: TokenHolder.fromAccountAddress(address),
+ recipient: CborAccountAddress.fromAccountAddress(address),
memo: memo ? CborMemo.fromString(memo) : undefined,
},
},
diff --git a/packages/browser-wallet/src/popup/popupX/pages/prompts/SendSponsoredTransaction/DisplaySingleTransferTokenUpdate.tsx b/packages/browser-wallet/src/popup/popupX/pages/prompts/SendSponsoredTransaction/DisplaySingleTransferTokenUpdate.tsx
new file mode 100644
index 000000000..af4eb9e30
--- /dev/null
+++ b/packages/browser-wallet/src/popup/popupX/pages/prompts/SendSponsoredTransaction/DisplaySingleTransferTokenUpdate.tsx
@@ -0,0 +1,346 @@
+import React, { useContext, useEffect, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import {
+ AccountInfo,
+ HexString,
+ isRejectTransaction,
+ isSuccessTransaction,
+ TokenUpdatePayload,
+ TransactionHash,
+ TransactionSummaryType,
+} from '@concordium/web-sdk';
+import { Cbor, CborMemo, TokenOperationType, TokenTransferOperation } from '@concordium/web-sdk/plt';
+import { WalletCredential } from '@shared/storage/types';
+import { displaySplitAddress, useCredential, useIdentityName } from '@popup/shared/utils/account-helpers';
+import { useAccountInfo } from '@popup/shared/AccountInfoListenerContext';
+import { formatTokenAmount } from '@popup/popupX/shared/utils/helpers';
+import { displayUrl } from '@popup/shared/utils/string-helpers';
+import { logError } from '@shared/utils/log-helpers';
+import { LoaderInline } from '@popup/popupX/shared/Loader';
+import { fullscreenPromptContext } from '@popup/popupX/page-layouts/FullscreenPromptLayout';
+import { useAtomValue, useSetAtom } from 'jotai';
+import { addToastAtom } from '@popup/state';
+import { grpcClientAtom } from '@popup/store/settings';
+import { useAsyncMemo } from 'wallet-common-helpers';
+import Page from '@popup/popupX/shared/Page';
+import Text from '@popup/popupX/shared/Text';
+import Card from '@popup/popupX/shared/Card';
+import Button from '@popup/popupX/shared/Button';
+import Label from '@popup/popupX/shared/Label';
+import Tooltip from '@popup/popupX/shared/Tooltip/Tooltip';
+import QuestionIcon from '@assets/svgX/UiKit/Interface/circled-question-mark.svg';
+import CheckCircle from '@assets/svgX/check-circle.svg';
+import Cross from '@assets/svgX/close.svg';
+
+interface Status {
+ type?: 'success' | 'failure';
+}
+
+function TransactionStatusIcon({ status }: { status?: Status }) {
+ const { t } = useTranslation('x', { keyPrefix: 'prompts.sendTransactionX.sponsored' });
+ switch (status?.type) {
+ case undefined: {
+ return (
+ <>
+
+ {t('pending.label')}
+ >
+ );
+ }
+ case 'success': {
+ return (
+ <>
+
+ {t('success.label')}
+ >
+ );
+ }
+ case 'failure': {
+ return (
+ <>
+
+ {t('failure.label')}
+ >
+ );
+ }
+ default:
+ throw new Error('Unexpected status');
+ }
+}
+
+type TransactionStatusProps = {
+ status?: Status;
+ amount?: string | number;
+ sponsorAccount?: string;
+ tokenName?: string;
+};
+
+function TransactionStatus({ status, amount, sponsorAccount, tokenName }: TransactionStatusProps) {
+ const { t } = useTranslation('x', { keyPrefix: 'prompts.sendTransactionX.sponsored' });
+
+ return (
+ <>
+
+ {t('tokenNameAmount', { tokenName })}
+ {amount}
+ {t('transactionFee')}:
+