Skip to content
Open
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
51 changes: 29 additions & 22 deletions skills/dapp/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,8 @@ export const rpc = new StellarSdk.rpc.Server(config.rpcUrl);
import { useState, useEffect, useCallback } from "react";
import {
isConnected,
isAllowed,
setAllowed,
getPublicKey,
getAddress,
requestAccess,
signTransaction,
getNetwork,
} from "@stellar/freighter-api";
Expand All @@ -130,34 +129,38 @@ export function useFreighter() {
}, []);

const checkConnection = async () => {
const freighterConnected = await isConnected();
if (!freighterConnected) return;

const allowed = await isAllowed();
if (allowed) {
const pubKey = await getPublicKey();
const net = await getNetwork();
setConnected(true);
setAddress(pubKey);
setNetwork(net);
}
const { isConnected: installed, error } = await isConnected();
if (error || !installed) return;

// getAddress returns address: "" until the app has been granted access,
// so a non-empty address means we're already authorized.
const { address: addr, error: addressError } = await getAddress();
if (addressError || !addr) return;

const { network: net, error: networkError } = await getNetwork();
if (networkError) return;
setConnected(true);
setAddress(addr);
setNetwork(net);
Comment on lines +132 to +144
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Fixed in 9abdab8. The hook snippet now handles error from getAddress()/getNetwork() and validates address/network before setting connected state.

};

const connect = useCallback(async () => {
const freighterConnected = await isConnected();
if (!freighterConnected) {
const { isConnected: installed, error } = await isConnected();
if (error || !installed) {
throw new Error("Freighter extension not installed");
}

await setAllowed();
const pubKey = await getPublicKey();
const net = await getNetwork();
// requestAccess prompts the user and returns the granted address.
const { address: addr, error: accessError } = await requestAccess();
if (accessError) throw new Error(accessError.message);

const { network: net, error: networkError } = await getNetwork();
if (networkError) throw new Error(networkError.message);
setConnected(true);
setAddress(pubKey);
setAddress(addr);
setNetwork(net);

return pubKey;
return addr;
}, []);

const disconnect = useCallback(() => {
Expand All @@ -169,7 +172,11 @@ export function useFreighter() {
const sign = useCallback(
async (xdr: string, networkPassphrase: string) => {
if (!connected) throw new Error("Wallet not connected");
return signTransaction(xdr, { networkPassphrase });
const { signedTxXdr, error } = await signTransaction(xdr, {
networkPassphrase,
});
if (error) throw new Error(error.message);
return signedTxXdr;
},
[connected]
);
Expand Down
48 changes: 25 additions & 23 deletions skills/soroban/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ use soroban_sdk::Env;
#[test]
fn test_increment() {
let env = Env::default();
let contract_id = env.register_contract(None, CounterContract);
let contract_id = env.register(CounterContract, ());
let client = CounterContractClient::new(&env, &contract_id);

let admin = Address::generate(&env);
Expand All @@ -524,7 +524,7 @@ fn test_transfer_with_auth() {
let env = Env::default();
env.mock_all_auths(); // Auto-approve all auth requests

let contract_id = env.register_contract(None, TokenContract);
let contract_id = env.register(TokenContract, ());
let client = TokenContractClient::new(&env, &contract_id);

let alice = Address::generate(&env);
Expand Down Expand Up @@ -631,7 +631,7 @@ fn test_basic_functionality() {
let env = Env::default();

// Register contract
let contract_id = env.register_contract(None, Contract);
let contract_id = env.register(Contract, ());

// Create typed client
let client = ContractClient::new(&env, &contract_id);
Expand All @@ -657,7 +657,7 @@ fn test_with_auth() {
// Mock all authorizations automatically
env.mock_all_auths();

let contract_id = env.register_contract(None, TokenContract);
let contract_id = env.register(TokenContract, ());
let client = TokenContractClient::new(&env, &contract_id);

let admin = Address::generate(&env);
Expand Down Expand Up @@ -687,7 +687,7 @@ fn test_with_auth() {
#[test]
fn test_specific_auth() {
let env = Env::default();
let contract_id = env.register_contract(None, Contract);
let contract_id = env.register(Contract, ());
let client = ContractClient::new(&env, &contract_id);

let user = Address::generate(&env);
Expand All @@ -713,7 +713,7 @@ fn test_specific_auth() {
#[test]
fn test_time_based() {
let env = Env::default();
let contract_id = env.register_contract(None, VestingContract);
let contract_id = env.register(VestingContract, ());
let client = VestingContractClient::new(&env, &contract_id);

let beneficiary = Address::generate(&env);
Expand Down Expand Up @@ -762,7 +762,7 @@ fn test_ledger_manipulation() {
#[test]
fn test_events() {
let env = Env::default();
let contract_id = env.register_contract(None, Contract);
let contract_id = env.register(Contract, ());
let client = ContractClient::new(&env, &contract_id);

client.do_something();
Expand All @@ -786,7 +786,7 @@ fn test_events() {
#[test]
fn test_storage_ttl() {
let env = Env::default();
let contract_id = env.register_contract(None, Contract);
let contract_id = env.register(Contract, ());
let client = ContractClient::new(&env, &contract_id);

client.store_data();
Expand All @@ -809,8 +809,8 @@ fn test_cross_contract() {
let env = Env::default();

// Register both contracts
let token_id = env.register_contract_wasm(None, token::WASM);
let vault_id = env.register_contract(None, VaultContract);
let token_id = env.register(token::WASM, ());
let vault_id = env.register(VaultContract, ());

let token_client = token::Client::new(&env, &token_id);
let vault_client = VaultContractClient::new(&env, &vault_id);
Expand Down Expand Up @@ -921,8 +921,8 @@ stellar contract deploy \
--source my-testnet-key \
--network testnet

# Install contract code (separate from deployment)
stellar contract install \
# Upload contract code (separate from deployment)
stellar contract upload \
--wasm target/wasm32-unknown-unknown/release/contract.wasm \
--source my-testnet-key \
--network testnet
Expand Down Expand Up @@ -1131,7 +1131,7 @@ use soroban_sdk::{testutils::Address as _, Address, Env};
use crate::{Contract, ContractClient};

pub fn setup_contract(env: &Env) -> (Address, ContractClient) {
let contract_id = env.register_contract(None, Contract);
let contract_id = env.register(Contract, ());
let client = ContractClient::new(env, &contract_id);
let admin = Address::generate(env);

Expand Down Expand Up @@ -1274,7 +1274,7 @@ fn test_upgrade_compatibility() {
env.mock_all_auths();

// Register both versions
let old_id = env.register_contract_wasm(None, deployed::WASM);
let old_id = env.register(deployed::WASM, ());
let new_id = env.register(NewContract, ());

let old_client = deployed::Client::new(&env, &old_id);
Expand Down Expand Up @@ -2407,22 +2407,23 @@ const preparedTx = StellarSdk.rpc.assembleTransaction(

**Solution**:
```typescript
import { isConnected, isAllowed } from "@stellar/freighter-api";
import { isConnected, isAllowed, requestAccess } from "@stellar/freighter-api";

async function checkFreighter() {
// Check if extension is installed
const connected = await isConnected();
if (!connected) {
const { isConnected: installed, error } = await isConnected();
if (error || !installed) {
// Prompt user to install
Comment on lines +2410 to 2416
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Fixed in 9abdab8. Updated the network mismatch snippet to use Freighter v3 getNetwork() object return shape and handle error.

window.open("https://freighter.app", "_blank");
return;
}

// Check if app is allowed
const allowed = await isAllowed();
if (!allowed) {
// Need to request permission
await setAllowed();
// Check if this app is already authorized
const { isAllowed: granted } = await isAllowed();
if (!granted) {
// requestAccess prompts the user and returns { address, error }
const { error: accessError } = await requestAccess();
if (accessError) throw new Error(accessError.message);
}
}
```
Expand All @@ -2442,7 +2443,8 @@ async function checkFreighter() {
import { getNetwork } from "@stellar/freighter-api";

async function validateNetwork() {
const walletNetwork = await getNetwork();
const { network: walletNetwork, error } = await getNetwork();
if (error) throw new Error(error.message);
const appNetwork = process.env.NEXT_PUBLIC_STELLAR_NETWORK;

if (walletNetwork !== appNetwork) {
Expand Down
2 changes: 1 addition & 1 deletion skills/standards/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ Use the CAP preamble status fields as the source of truth for implementation rea
- Contract implementation details: [`../soroban/SKILL.md`](../soroban/SKILL.md)
- Advanced architecture guidance: [`../soroban/SKILL.md`](../soroban/SKILL.md)
- RPC and data access: [`../data/SKILL.md`](../data/SKILL.md)
- Security considerations: [`../security/SKILL.md`](../security/SKILL.md)
- Security considerations: [`../soroban/SKILL.md`](../soroban/SKILL.md#part-3-security)

---

Expand Down