Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
3 changes: 2 additions & 1 deletion examples/wallet-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
28 changes: 21 additions & 7 deletions examples/wallet-api/sign-token-update.html
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
{
Expand All @@ -40,7 +47,7 @@
value: '1000000',
decimals: 6,
}),
recipient: TokenHolder.fromAccountAddress(
recipient: CborAccountAddress.fromAccountAddress(
AccountAddress.fromBase58('4PVJxxtiNVanfuNgvFWtFT1JCiqoVs6npVXBqwLNKprqnUiC2A')
),
memo: CborMemo.fromString('Some text for memo'),
Expand All @@ -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 = [
{
Expand All @@ -72,7 +86,7 @@
value: '1000000',
decimals: 6,
}),
recipient: TokenHolder.fromAccountAddress(
recipient: CborAccountAddress.fromAccountAddress(
AccountAddress.fromBase58('4PVJxxtiNVanfuNgvFWtFT1JCiqoVs6npVXBqwLNKprqnUiC2A')
),
memo: CborMemo.fromString('Some text for memo'),
Expand All @@ -95,7 +109,7 @@
value: '200000',
decimals: 6,
}),
recipient: TokenHolder.fromAccountAddress(
recipient: CborAccountAddress.fromAccountAddress(
AccountAddress.fromBase58('4PVJxxtiNVanfuNgvFWtFT1JCiqoVs6npVXBqwLNKprqnUiC2A')
),
memo: CborMemo.fromString('Some text for memo'),
Expand Down
158 changes: 158 additions & 0 deletions examples/wallet-api/sponsored-transaction.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<!DOCTYPE html>
<html>
<head>
<title>My cool dapp</title>
<script src="/sdk.js"></script>
<script src="/helpers.js"></script>
<script src="https://unpkg.com/cbor-web"></script>
<meta charset="utf-8" />
<script>
let currentAccountAddress = '';

async function submitPayloadToSponsor(sender, transaction) {
const provider = await concordiumHelpers.detectConcordiumProvider();
const {
buildBasicAccountSigner,
AccountAddress,
ConcordiumGRPCClient,
Transaction,
TransactionExpiry,
} = concordiumSDK;
const builder = Transaction.builderFromJSON(transaction);
const sponsorSigner = buildBasicAccountSigner(privateKey.value);
const sponsorSignerAccount = AccountAddress.fromBase58(sponsorAccount.value);

const grpcClient = new ConcordiumGRPCClient(await provider.grpcTransport);
const senderNonce = await grpcClient.getNextAccountNonce(sender);

const sponsorableTransaction = builder
.addMetadata({ sender, nonce: senderNonce.nonce, expiry: TransactionExpiry.futureMinutes(5) })
.addSponsor(sponsorSignerAccount)
.build();

const sponsored = await Transaction.sponsor(sponsorableTransaction, sponsorSigner);
return Transaction.toJSON(sponsored);
}

async function setupPage() {
const provider = await concordiumHelpers.detectConcordiumProvider();
provider.on('accountDisconnected', () => (currentAccountAddress = undefined));
provider.on('accountChanged', (accountAddress) => (currentAccountAddress = accountAddress));
provider.on('chainChanged', (chain) => alert(chain));

document.getElementById('connect').addEventListener('click', () => {
provider.connect().then((accountAddress) => {
currentAccountAddress = accountAddress;
document.getElementById('accountAddress').innerHTML = accountAddress;
});
});

document.getElementById('requestAccounts').addEventListener('click', () => {
provider.requestAccounts().then((accountAddresses) => {
currentAccountAddress = accountAddresses[0];
document.getElementById('accountAddress').innerHTML = accountAddresses;
});
});

document.getElementById('signSimpleTransfer').addEventListener('click', async () => {
const { AccountAddress, CcdAmount, Transaction } = concordiumSDK;

const payload = {
amount: CcdAmount.fromCcd(1000n),
toAddress: AccountAddress.fromBase58(recipient.value),
};

const transaction = Transaction.transfer(payload);

const sponsorResponse = await submitPayloadToSponsor(
AccountAddress.fromBase58(currentAccountAddress),
Transaction.toJSON(transaction)
);

console.log('sponsorResponse', sponsorResponse);

return provider
.sendSponsoredTransaction(currentAccountAddress, 3, sponsorResponse)
.then((sig) => alert(JSON.stringify(sig)))
.catch(alert);
Comment on lines 67 to 79
Copy link
Collaborator

Choose a reason for hiding this comment

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

And consequently

Suggested change
const sponsorResponse = await submitPayloadToSponsor(
AccountAddress.fromBase58(currentAccountAddress),
Transaction.toJSON(transaction)
);
console.log('sponsorResponse', sponsorResponse);
return provider
.sendSponsoredTransaction(currentAccountAddress, 3, sponsorResponse)
.then((sig) => alert(JSON.stringify(sig)))
.catch(alert);
const sponsorResponse = await submitPayloadToSponsor(
AccountAddress.fromBase58(currentAccountAddress),
Transaction.toJSON(transaction)
);
const sponsoredParsed = Transaction.signableFromJSON(sponsorResponse);
console.log('sponsorResponse', sponsorResponse);
return provider
.sendSponsoredTransaction(currentAccountAddress, sponsoredParsed)
.then((sig) => alert(JSON.stringify(sig)))
.catch(alert);

});

document.getElementById('signTokenUpdate').addEventListener('click', async () => {
const {
AccountAddress,
TokenOperationType,
TokenAmount,
CborMemo,
Cbor,
TokenId,
Transaction,
CborAccountAddress,
} = concordiumSDK;

const ops = [
{
[TokenOperationType.Transfer]: {
amount: TokenAmount.fromJSON({
value: '1000000',
decimals: 6,
}),
recipient: CborAccountAddress.fromAccountAddress(
AccountAddress.fromBase58(recipient.value)
),
memo: CborMemo.fromString('Some text for memo'),
},
},
];

console.log(JSON.stringify(ops));

const payload = {
tokenId: TokenId.fromString('EURtest'),
operations: Cbor.encode(ops),
};

const transaction = Transaction.tokenUpdate(payload);

console.log(JSON.stringify(payload));

const sponsorResponse = await submitPayloadToSponsor(
AccountAddress.fromBase58(currentAccountAddress),
Transaction.toJSON(transaction)
);

console.log('sponsorResponse', sponsorResponse);

return provider
.sendSponsoredTransaction(currentAccountAddress, 27, sponsorResponse)
.then((sig) => alert(JSON.stringify(sig)))
.catch(alert);
Comment on lines 120 to 132
Copy link
Collaborator

Choose a reason for hiding this comment

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

And here

Suggested change
const sponsorResponse = await submitPayloadToSponsor(
AccountAddress.fromBase58(currentAccountAddress),
Transaction.toJSON(transaction)
);
console.log('sponsorResponse', sponsorResponse);
return provider
.sendSponsoredTransaction(currentAccountAddress, 27, sponsorResponse)
.then((sig) => alert(JSON.stringify(sig)))
.catch(alert);
const sponsorResponse = await submitPayloadToSponsor(
AccountAddress.fromBase58(currentAccountAddress),
Transaction.toJSON(transaction)
);
console.log('sponsorResponse', sponsorResponse);
const sponsoredParsed = Transaction.signableFromJSON(sponsorResponse);
return provider
.sendSponsoredTransaction(currentAccountAddress, sponsoredParsed)
.then((sig) => alert(JSON.stringify(sig)))
.catch(alert);

});
}
setupPage();
</script>
</head>

<body>
<div>
<button id="connect">Connect</button>
<h3 id="accountAddress" style="display: inline">Account address:</h3>
</div>
<br />
<button id="requestAccounts">Request accounts</button>
<br />
<h3>Sponsor Config:</h3>
Sponsor Account:
<input type="text" id="sponsorAccount" value="3PCRkKoFrSzgUmzfFF7C17pc3wm9wrVgbaCrbgGs6UMxy5aogo" />
<br />
Sponsor Private Key:
<input type="text" id="privateKey" value="9a494111614cd737ed89e500a3d155af80a4f8faf18f168568f8facbd2be3909" />
<br />
<h3>Recipient Config:</h3>
Recipient Address:
<input type="text" id="recipient" value="4jPwPo6da7g43DRQFcwMxJQQA9BbD762fbSS24X8Z7VHkvnehe" />
<br />
<br />
<button id="signSimpleTransfer">Sign Simple Transfer</button>
<button id="signTokenUpdate">Sign PLT Transfer</button>
</body>
</html>
16 changes: 16 additions & 0 deletions packages/browser-wallet-api-helpers/src/wallet-api-types.ts
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would be good to create a 3.1.0-alpha release of this to use alongside the test build of the wallet produced from this PR. I suggest you do the following to do this:

  1. merge in main branch
  2. git tag api-helpers/3.1.0-alpha && git push --tags origin api-helpers/3.1.0-alpha to create the tag needed to trigger the release

The release workflow is untested currently, so if the release job fails we need to look into a fix.

Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -69,6 +70,8 @@ export type SendTransactionPayload =
| SendTransactionUpdateContractPayload
| SendTransactionInitContractPayload;

export type SignableTransaction = Transaction.Signable;

export type SmartContractParameters =
| { [key: string]: SmartContractParameters }
| SmartContractParameters[]
Expand Down Expand Up @@ -254,6 +257,19 @@ interface MainWalletApi {
type: LaxNumberEnumValue<AccountTransactionType.ConfigureDelegation>,
payload: ConfigureDelegationPayload
): Promise<string>;
/**
* 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 type the type of transaction that is to be signed and sent.
* @param payload the sponsored transaction with header to be signed and sent.
*/
sendSponsoredTransaction(
accountAddress: AccountAddressSource,
type: LaxNumberEnumValue<AccountTransactionType.Transfer>,
payload: SignableTransaction
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a good reason to have the type as an argument as well? It's used in sendTransaction to give better type information with regards to which payload is supported for a transaction type. In the case of the SignableTransaction the transaction type is already available in the payload of the transaction.

Suggested change
type: LaxNumberEnumValue<AccountTransactionType.Transfer>,
payload: SignableTransaction
payload: SignableTransaction

See

): Promise<string>;
/**
* 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.
Expand Down
2 changes: 2 additions & 0 deletions packages/browser-wallet/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Added support for Sponsored Transactions handling. With new method `sendSponsoredTransaction` in wallet-api.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
- Added support for Sponsored Transactions handling. With new method `sendSponsoredTransaction` in wallet-api.
## 2.7.1
- Added support for Sponsored Transactions handling. With new method `sendSponsoredTransaction` in wallet-api.


## 2.7.0

### Changed
Expand Down
2 changes: 1 addition & 1 deletion packages/browser-wallet/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@concordium/browser-wallet",
"private": true,
"version": "2.7.0",
"version": "2.7.1",
"description": "Browser extension wallet for the Concordium blockchain",
"author": "Concordium Software",
"license": "Apache-2.0",
Expand Down
8 changes: 8 additions & 0 deletions packages/browser-wallet/src/background/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions packages/browser-wallet/src/messaging/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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',
Expand Down
5 changes: 5 additions & 0 deletions packages/browser-wallet/src/popup/constants/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: '/',
Expand Down Expand Up @@ -63,6 +65,9 @@ export const relativeRoutes = {
sendTransaction: {
path: 'send-transaction',
},
sendSponsoredTransaction: {
path: 'send-sponsored-transaction',
},
endIdentityIssuance: {
path: 'end-identity-issuance',
},
Expand Down
3 changes: 3 additions & 0 deletions packages/browser-wallet/src/popup/popupX/constants/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,9 @@ export const relativeRoutes = {
sendTransaction: {
path: 'sendTransaction',
},
sendSponsoredTransaction: {
path: 'sendSponsoredTransaction',
},
addWeb3IdCredential: {
path: 'add-web3id-credential',
},
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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,
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
},
},
Expand Down
Loading