Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
10 changes: 10 additions & 0 deletions @stellar/typescript-wallet-sdk-km/CHANGELOG.MD
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# Release notes - Typescript Wallet SDK Key Manager - 6.0.0

### BREAKING CHANGES
* `KeyManager.fetchAuthToken` rejects a challenge whose `client_domain` operation has no source account or uses the server account as its source, throwing stellar-sdk's `InvalidChallengeError`. stellar-sdk 17.3's `WebAuth.readChallengeTx` added this check, since SEP-10 requires that source to be the client domain's signing key. 5.1.0 accepted such a challenge

### Added
* Protocol 30 support: upgraded `@stellar/stellar-sdk` from `17.0.1` to `17.4.0`, which adds CAP-84 muxed contract (`W…`) addresses. The Albedo and Freighter handlers read back a signed transaction that contains one. Before, decoding the signed envelope threw

**Consumer note:** the key-store surface (`KeyManager`'s `registerEncrypter`, `storeKey`, `loadKey`, `loadAllKeyIds`, `removeKey` and `changePassword`, the keystores and the encrypters) doesn't use stellar-sdk and is unchanged, so upgrading from 5.x needs no code changes for it. Consumers that also depend on `@stellar/stellar-sdk` directly should align their pin to `17.4.0`, since this package bundles that exact version.

# Release notes - Typescript Wallet SDK Key Manager - 5.1.0

### Changed
Expand Down
4 changes: 2 additions & 2 deletions @stellar/typescript-wallet-sdk-km/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@stellar/typescript-wallet-sdk-km",
"version": "5.1.0",
"version": "6.0.0",
"engines": {
"node": ">=22.12.0"
},
Expand Down Expand Up @@ -40,7 +40,7 @@
"@stablelib/base64": "^2.0.0",
"@stablelib/utf8": "^2.0.0",
"@stellar/freighter-api": "6.0.1",
"@stellar/stellar-sdk": "17.0.1",
"@stellar/stellar-sdk": "17.4.0",
"@trezor/connect-plugin-stellar": "^9.0.2",
"bignumber.js": "^9.1.2",
"scrypt-async": "^2.0.1",
Expand Down
21 changes: 21 additions & 0 deletions @stellar/typescript-wallet-sdk-km/test/albedo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ import {
import albedo from "@albedo-link/intent";

import { albedoSigningWith } from "./fixtures/albedo";
import {
MUXED_CONTRACT_MAX_ID,
buildTransferToMuxedContract,
transferRecipient,
} from "./fixtures/muxedContract";
import { albedoHandler } from "../src/Handlers/albedo";
import { Key, KeyType } from "../src/Types";

Expand Down Expand Up @@ -154,6 +159,22 @@ describe("albedoHandler", () => {
},
);

it("returns the signed transaction with its muxed contract (W) recipient intact", async () => {
const tx = buildTransferToMuxedContract(
signer.publicKey(),
Networks.TESTNET,
);
const key = makeAlbedoKey(signer, { network: Networks.TESTNET });

const result = await albedoHandler.signTransaction({
transaction: tx,
key,
});

expect(transferRecipient(result)).toBe(MUXED_CONTRACT_MAX_ID);
expect(WebAuth.verifyTxSignedBy(result, signer.publicKey())).toBe(true);
});

it("refuses a key that holds a private key without opening Albedo", async () => {
const tx = buildTestTransaction(signer, Networks.TESTNET);
const key = makeAlbedoKey(signer, { privateKey: signer.secret() });
Expand Down
54 changes: 54 additions & 0 deletions @stellar/typescript-wallet-sdk-km/test/fixtures/muxedContract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {
Account,
Address,
Asset,
BASE_FEE,
Operation,
Transaction,
TransactionBuilder,
nativeToScVal,
} from "@stellar/stellar-sdk";

// A Protocol 30 (CAP-84) muxed contract address: SEP-23's test vector
// contract with the largest mux ID, 2^64 - 1.
export const MUXED_CONTRACT_MAX_ID =
"WA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJV7777777777777GMO";

// A native XLM Stellar Asset Contract transfer from `source` to
// MUXED_CONTRACT_MAX_ID.
export function buildTransferToMuxedContract(
source: string,
networkPassphrase: string,
): Transaction {
return new TransactionBuilder(new Account(source, "100"), {
fee: BASE_FEE,
networkPassphrase,
})
.addOperation(
Operation.invokeContractFunction({
contract: Asset.native().contractId(networkPassphrase),
function: "transfer",
args: [
new Address(source).toScVal(),
new Address(MUXED_CONTRACT_MAX_ID).toScVal(),
nativeToScVal(BigInt(12345), { type: "i128" }),
],
}),
)
.setTimeout(30)
.build();
}

// Reads the `to` argument of a transaction built by
// buildTransferToMuxedContract, as a strkey.
export function transferRecipient(tx: Transaction): string {
const { func } = tx.operations[0] as Operation.InvokeHostFunction;
if (func.type !== "hostFunctionTypeInvokeContract") {
throw new Error(`expected a contract invocation, not ${func.type}`);
}
const to = func.invokeContract.args[1];
if (to.type !== "scvAddress") {
throw new Error(`expected an address, not ${to.type}`);
}
return Address.fromScAddress(to.address).toString();
}
36 changes: 36 additions & 0 deletions @stellar/typescript-wallet-sdk-km/test/freighter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ import {
Transaction,
Asset,
BASE_FEE,
WebAuth,
} from "@stellar/stellar-sdk";
import freighterApi from "@stellar/freighter-api";

import {
MUXED_CONTRACT_MAX_ID,
buildTransferToMuxedContract,
transferRecipient,
} from "./fixtures/muxedContract";
import { freighterHandler } from "../src/Handlers/freighter";
import { Key, KeyType } from "../src/Types";

Expand Down Expand Up @@ -88,6 +94,36 @@ describe("freighterHandler", () => {
});
});

it("returns the signed transaction with its muxed contract (W) recipient intact", async () => {
const networkPassphrase = Networks.TESTNET;
const signer = Keypair.random();
const tx = buildTransferToMuxedContract(
signer.publicKey(),
networkPassphrase,
);
const key = makeFreighterKey({ publicKey: signer.publicKey() });

// Freighter answers with the envelope it signed
const signed = TransactionBuilder.fromXdr(
tx.toXdr(),
networkPassphrase,
) as Transaction;
signed.sign(signer);
mockSignTransaction.mockResolvedValue({
signedTxXdr: signed.toXdr(),
signerAddress: signer.publicKey(),
});

const result = await freighterHandler.signTransaction({
transaction: tx,
key,
custom: { networkPassphrase },
});

expect(transferRecipient(result)).toBe(MUXED_CONTRACT_MAX_ID);
expect(WebAuth.verifyTxSignedBy(result, signer.publicKey())).toBe(true);
});

it("passes custom.address through to freighter", async () => {
const networkPassphrase = Networks.TESTNET;
const tx = buildTestTransaction(networkPassphrase);
Expand Down
98 changes: 98 additions & 0 deletions @stellar/typescript-wallet-sdk-km/test/keyManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,104 @@ describe("fetchAuthToken", () => {
expect(fetchSpy).toHaveBeenCalledTimes(1);
});

// WebAuth.readChallengeTx checks these since stellar-sdk 17.3. SEP-10
// requires the client_domain operation's source to be the client domain's
// signing key.
test.each([
["has no source account", "none"],
["uses the server account as its source", "server"],
])(
"Rejects a challenge whose client_domain operation %s",
async (_, clientDomainSource) => {
const authServer = "https://www.stellar.org/auth";
const password = "very secure password";

const keyNetwork = Networks.TESTNET;

const accountKey = Keypair.random();
const account = new Account(accountKey.publicKey(), "-1");

// set up the manager
const testStore = new MemoryKeyStore();
const testKeyManager = new KeyManager({
keyStore: testStore,
});

testKeyManager.registerEncrypter(IdentityEncrypter);

const keypair = Keypair.master(keyNetwork);

const tx = new TransactionBuilder(account, {
fee: BASE_FEE,
networkPassphrase: keyNetwork,
})
.addOperation(
Operation.manageData({
name: `stellar.org auth`,
value: randomBytes(48).toString("base64"),
source: keypair.publicKey(),
}),
)
.addOperation(
Operation.manageData({
name: "web_auth_domain",
value: new URL(authServer).hostname,
source: account.accountId(),
}),
)
.addOperation(
Operation.manageData({
name: "client_domain",
value: "wallet.example.com",
source:
clientDomainSource === "server" ? account.accountId() : undefined,
}),
)
.setTimeout(300)
.build();

tx.sign(accountKey);

const fetchSpy = jest.spyOn(global, "fetch").mockResolvedValueOnce(
new Response(
JSON.stringify({
transaction: tx.toXDR(),
network_passphrase: keyNetwork,
}),
),
);

const keyMetadata = await testKeyManager.storeKey({
key: {
type: KeyType.plaintextKey,
publicKey: keypair.publicKey(),
privateKey: keypair.secret(),
network: keyNetwork,
},
password,
encrypterName: "IdentityEncrypter",
});

const onChallengeTransactionSignature = jest.fn();

const result = testKeyManager.fetchAuthToken({
id: keyMetadata.id,
password,
authServer,
authServerKey: account.accountId(),
authServerHomeDomains: ["stellar.org"],
clientDomain: "wallet.example.com",
onChallengeTransactionSignature,
});
await expect(result).rejects.toThrow(WebAuth.InvalidChallengeError);
await expect(result).rejects.toThrow(/client_domain/);

// The challenge was fetched, but nothing was signed or posted back.
expect(fetchSpy).toHaveBeenCalledTimes(1);
expect(onChallengeTransactionSignature).not.toHaveBeenCalled();
},
);

test("Can use a challenge token", async () => {
const authServer = "https://www.stellar.org/auth";
const password = "very secure password";
Expand Down
11 changes: 11 additions & 0 deletions @stellar/typescript-wallet-sdk-soroban/CHANGELOG.MD
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
# Release notes - Typescript Wallet SDK Soroban - 6.0.0

### BREAKING CHANGES
* `scValByType` throws a `TypeError` for a map, at any depth, with two keys that convert to the same JSON key, such as `u32 1` and `symbol "1"`. It used to render the map without one of those entries. The change comes from stellar-sdk 17.3's `scValToNative`

### Added
* Protocol 30 support: upgraded `@stellar/stellar-sdk` from `17.0.1` to `17.4.0`, which adds CAP-84 muxed contract (`W…`) addresses. `scValByType` renders one as its `W…` address, and `getTokenInvocationArgs` returns it as `to` for a Stellar Asset Contract `transfer` or `mint` to it. Before, a transaction that contained one failed to decode

### Fixed
* `getInvocationDetails` shows a Stellar Asset Contract's asset with its code as encoded, so an asset whose code is `xlm` shows as `xlm:G…`. It used to show as `XLM:G…`, the same as an asset whose code is `XLM`. The fix comes from stellar-sdk 17.3

# Release notes - Typescript Wallet SDK Soroban - 5.1.0
* Version bump

Expand Down
5 changes: 5 additions & 0 deletions @stellar/typescript-wallet-sdk-soroban/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ const args = getTokenInvocationArgs(op);
to: "GDUBMXMABE7UOZSGYJ5ONE7UYAEHKK3JOX7HZQGNZ7NYTZPPP4AJ2GQJ",
amount: 5
}

`to` is the recipient as the invocation names it, so it can also be a muxed
account (M...) or, since Protocol 30, a muxed contract (W...) address.
*/

const formattedAmount = formatTokenAmount("10000123", 3);
Expand All @@ -77,4 +80,6 @@ const accountAddress = xdr.ScVal.scvAddress(
const addressString = scValByType(accountAddress);
// converts smart contract complex value into a simple string
// addressString = "GBBM6BKZPEHWYO3E3YKREDPQXMS4VK35YLNU7NFBRI26RAN7GI5POFBB"
// Every address type renders as its strkey, including Protocol 30 muxed
// contract (W...) addresses.
```
4 changes: 2 additions & 2 deletions @stellar/typescript-wallet-sdk-soroban/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@stellar/typescript-wallet-sdk-soroban",
"version": "5.1.0",
"version": "6.0.0",
"engines": {
"node": ">=22.12.0"
},
Expand Down Expand Up @@ -30,7 +30,7 @@
"webpack-cli": "^5.1.4"
},
"dependencies": {
"@stellar/stellar-sdk": "17.0.1"
"@stellar/stellar-sdk": "17.4.0"
},
"scripts": {
"prepare": "husky install",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import { Address, scValToNative, xdr } from "@stellar/stellar-sdk";
* @returns the smart contract value converted to a common/simpler
* value like string, array, hex string, JSON string, etc.
*
* @throws {TypeError} when a map, at any depth, has two keys that convert to
* the same JSON key, such as u32 1 and symbol "1", instead of rendering it
* without one of them.
*
* @example
* const accountAddress = xdr.ScVal.scvAddress(
* new Address("GBBM6BKZPEHWYO3E3YKREDPQXMS4VK35YLNU7NFBRI26RAN7GI5POFBB").toScAddress(),
Expand All @@ -21,8 +25,8 @@ import { Address, scValToNative, xdr } from "@stellar/stellar-sdk";
export const scValByType = (scVal: xdr.ScVal) => {
switch (scVal.type) {
case "scvAddress":
// Address.fromScAddress covers all five ScAddress variants, so no
// per-variant branching is needed here.
// Address.fromScAddress covers every ScAddress variant, muxed contract
// (W...) addresses included, so no per-variant branching is needed here.
return Address.fromScAddress(scVal.address).toString();

case "scvBool":
Expand Down
2 changes: 2 additions & 0 deletions @stellar/typescript-wallet-sdk-soroban/src/Types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export enum SorobanTokenInterface {

export type ArgsForTokenInvocation = {
from: string;
// The recipient as the invocation names it, so it can also be a muxed
// account (M...) or, since Protocol 30, a muxed contract (W...) address.
to: string;
amount: bigint | number;
};
Expand Down
Loading
Loading