diff --git a/skills/dapp/SKILL.md b/skills/dapp/SKILL.md index 0e0d105..d67f712 100644 --- a/skills/dapp/SKILL.md +++ b/skills/dapp/SKILL.md @@ -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"; @@ -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); }; 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(() => { @@ -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] ); diff --git a/skills/soroban/SKILL.md b/skills/soroban/SKILL.md index 1054217..3c98cb8 100644 --- a/skills/soroban/SKILL.md +++ b/skills/soroban/SKILL.md @@ -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); @@ -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); @@ -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); @@ -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); @@ -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); @@ -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); @@ -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(); @@ -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(); @@ -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); @@ -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 @@ -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); @@ -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); @@ -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 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); } } ``` @@ -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) { diff --git a/skills/standards/SKILL.md b/skills/standards/SKILL.md index a465360..08d4dcd 100644 --- a/skills/standards/SKILL.md +++ b/skills/standards/SKILL.md @@ -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) ---