This guide shows how to build an automated audit logging system in Spring Boot using AOP and JPA, enabling seamless tracking of every REST API action—method names, parameters, timestamps, and users—while keeping controllers clean and compliant-ready.This guide shows how to build an automated audit logging system in Spring Boot using AOP and JPA, enabling seamless tracking of every REST API action—method names, parameters, timestamps, and users—while keeping controllers clean and compliant-ready.

Spring Boot Audit Logs: Capture Every API Action Without Writing Boilerplate Code

2025/11/22 02:24
Okuma süresi: 10 dk

Audit logging is a crucial part of enterprise applications. Whether you’re building a banking platform, an insurance portal, or an e-commerce API, you must track who did what and when.

In this guide, we’ll build a fully functional Audit Logging system for a Spring Boot REST API. You’ll learn how to capture and persist audit logs automatically for every controller action — without manually adding log statements in each method.

What Is Audit Logging?

Audit logging records what actions were performed in your application, by whom, and when. \n In a REST API, audit logs are useful for:

  • Tracking who created, updated, or deleted resources
  • Investigating issues
  • Maintaining compliance or data integrity

We’ll build this with Spring Boot, JPA, and Aspect-Oriented Programming (AOP).

Real-World Use Case

Imagine a Product Management System where multiple users create, update, and delete products through REST APIs.

\n For compliance and debugging, you need to record:

  • Which user performed the action
  • What API method was called
  • Input parameters
  • Timestamp of the event

Instead of manually logging in every controller, we’ll use Spring AOP (Aspect-Oriented Programming) to intercept and persist audit logs automatically.

Project Structure

Below is the structure of our project:

auditlogging │ ├── src/main/java │ └── com.example.auditlogging │ ├── AuditloggingApplication.java │ ├── aspect/ │ │ └── AuditAspect.java │ ├── config/ │ │ ├── AsyncConfig.java │ │ └── SecurityConfig.java │ ├── controller/ │ │ └── ProductController.java │ ├── entity/ │ │ ├── AuditLog.java │ │ └── Product.java │ ├── filter/ │ │ └── CachingRequestResponseFilter.java │ ├── repository/ │ │ ├── AuditLogRepository.java │ │ └── ProductRepository.java │ └── service/ │ └── AuditService.java │ └── src/main/resources ├── application.properties ├── schema.sql └── data.sql

\

Step 1: Application Entry Point

AuditloggingApplication.java package com.example.auditlogging; import org.springframework.boot.SpringApplication; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableAspectJAutoProxy(proxyTargetClass = true) public class AuditloggingApplication { public static void main(String[] args) { SpringApplication.run(AuditloggingApplication.class, args); } }

Explanation

  • @EnableAspectJAutoProxy enables AOP features in Spring.
  • This class bootstraps the application and loads all beans.

Step 2: Entity Classes

AuditLog.java /** * */ package com.example.auditlogging.entity; /** * */ import jakarta.persistence.*; import java.time.LocalDateTime; @Entity @Table(name = "AUDIT_LOG") public class AuditLog { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String action; @Column(length = 2000) private String details; private String username; private LocalDateTime timestamp; public AuditLog() {} public AuditLog(String action, String details, String username, LocalDateTime timestamp) { this.action = action; this.details = details; this.username = username; this.timestamp = timestamp; } // Getters & Setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getAction() { return action; } public void setAction(String action) { this.action = action; } public String getDetails() { return details; } public void setDetails(String details) { this.details = details; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public LocalDateTime getTimestamp() { return timestamp; } public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; } }

Explanation

  • Represents the AUDIT_LOG table.
  • Stores method name, parameters, user, and timestamp.
  • This table will automatically capture entries whenever an API is called.
  • Columns
  • id: Primary key (auto-generated)
  • action: The controller method name
  • details: Information about the call (arguments, etc.)
  • username: Name of the user who performed the action
  • timestamp: When it happened

Product Entity

Product.java /** * */ package com.example.auditlogging.entity; /** * */ import jakarta.persistence.*; @Entity @Table(name = "product") public class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String category; private Double price; public Product() {} public Product(String name, String category, Double price) { this.name = name; this.category = category; this.price = price; } // Getters & Setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCategory() { return category; } public void setCategory(String category) { this.category = category; } public Double getPrice() { return price; } public void setPrice(Double price) { this.price = price; } }

Explanation

  • Represents a simple domain entity to perform CRUD operations.
  • The actions performed here will generate audit logs.

Step 3: Repository Layer

AuditLogRepository.java @Repository public interface AuditLogRepository extends JpaRepository<AuditLog, Long> { } ProductRepository.java @Repository public interface ProductRepository extends JpaRepository<Product, Long> {}

Explanation

  • Provides database operations for our entities.
  • Spring Data JPA auto-implements CRUD methods.

Step 4: Controller Layer

ProductController.java import com.example.auditlogging.entity.*; import com.example.auditlogging.repository.*; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/api/products") public class ProductController { private final ProductRepository productRepository; public ProductController(ProductRepository productRepository) { this.productRepository = productRepository; } @GetMapping public List<Product> getAllProducts() { return productRepository.findAll(); } @PostMapping public Product createProduct(@RequestBody Product product) { return productRepository.save(product); } @PutMapping("/{id}") public Product updateProduct(@PathVariable Long id, @RequestBody Product product) { product.setId(id); return productRepository.save(product); } @DeleteMapping("/{id}") public void deleteProduct(@PathVariable Long id) { productRepository.deleteById(id); } }

Explanation

  • A simple REST controller performing CRUD on Product.
  • Every method is intercepted by our AuditAspect.

This controller exposes four REST endpoints:

  • GET /api/products — fetch all products
  • POST /api/products — create product
  • PUT /api/products/{id} — update product
  • DELETE /api/products/{id} — delete product

Step 5: Aspect Layer — The Core Audit Logic

AuditAspect.java import com.example.auditlogging.entity.*; import com.example.auditlogging.repository.*; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.time.LocalDateTime; import java.util.Arrays; @Aspect @Component public class AuditAspect { private static final Logger logger = LoggerFactory.getLogger(AuditAspect.class); @Autowired private final AuditLogRepository auditLogRepository; public AuditAspect(AuditLogRepository auditLogRepository) { this.auditLogRepository = auditLogRepository; } // Pointcut to capture all controller methods // @Pointcut("within(com.example.auditdemo.controller..*)") @Pointcut("execution(* com.example.auditlogging.controller.ProductController.*(..))") public void controllerMethods() {} // After a successful return from any controller method @AfterReturning(value = "controllerMethods()", returning = "result") public void logAfter(JoinPoint joinPoint, Object result) { try { String method = joinPoint.getSignature().getName(); String args = Arrays.toString(joinPoint.getArgs()); AuditLog log = new AuditLog(); log.setAction(method.toUpperCase()); log.setDetails("Method " + method + " executed with args " + args); log.setTimestamp(LocalDateTime.now()); log.setUsername("system"); auditLogRepository.save(log); // System.out.println("✅ Audit log saved for " + method); logger.info("✅ Audit log saved successfully for method: {}", method); } catch (Exception e) { System.err.println("❌ Error saving audit log: " + e.getMessage()); logger.error("⚠️ Failed to save audit log: {}", e.getMessage()); e.printStackTrace(); } } }

Explanation

  • Uses AspectJ annotations to intercept all controller methods.
  • @AfterReturning runs after a method successfully returns.
  • Builds an AuditLog entry from method name and arguments.
  • Persists the audit log using JPA repository.
  • Prints a log message on success or failure.
  • @Aspect defines this class as an aspect.
  • @Pointcut selects which methods to intercept (here, all controller methods).
  • Saves all audit details to AUDIT_LOG.

Step 6: Asynchronous Configuration

AsyncConfig.java @Configuration @EnableAsync public class AsyncConfig { @Bean(name = "auditExecutor") public Executor auditExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(2); executor.setMaxPoolSize(5); executor.setQueueCapacity(500); executor.setThreadNamePrefix("audit-"); executor.initialize(); return executor; }

Explanation

  • Configures a thread pool for background tasks (audit logging can be made async).
  • Improves performance for high-traffic APIs.

Step 7: Optional — HTTP Request/Response Caching

CachingRequestResponseFilter.java @Component public class CachingRequestResponseFilter implements Filter { public static final String CORRELATION_ID_HEADER = "X-Correlation-Id"; @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request); ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(response); String correlationId = request.getHeader(CORRELATION_ID_HEADER); if (correlationId == null || correlationId.isBlank()) { correlationId = UUID.randomUUID().toString(); } wrappedResponse.setHeader(CORRELATION_ID_HEADER, correlationId); long start = System.currentTimeMillis(); chain.doFilter(wrappedRequest, wrappedResponse); long duration = System.currentTimeMillis() - start; request.setAttribute("audit.correlationId", correlationId); request.setAttribute("audit.durationMs", duration); wrappedResponse.copyBodyToResponse(); } }

Explanation

  • Adds a X-Correlation-Id header for tracing.
  • Measures execution time for each request.
  • Enhances observability when debugging logs.
  • Adds both as request attributes so they can appear in audit details.
  • Returns them in the HTTP response headers.

Step 8: Database Setup

schema.sql CREATE TABLE IF NOT EXISTS audit_log ( id BIGINT AUTO_INCREMENT PRIMARY KEY, method VARCHAR(255), endpoint VARCHAR(500), http_method VARCHAR(50), status VARCHAR(255), execution_time_ms BIGINT, timestamp TIMESTAMP ); data.sql -- Insert sample products INSERT INTO product (name, category, price) VALUES ('iPhone 15', 'Electronics', 1299.99); INSERT INTO product (name, category, price) VALUES ('Samsung Galaxy S24', 'Electronics', 1199.50); INSERT INTO product (name, category, price) VALUES ('MacBook Pro 14"', 'Computers', 2499.00); INSERT INTO product (name, category, price) VALUES ('Dell XPS 13', 'Computers', 1399.00); INSERT INTO product (name, category, price) VALUES ('Sony WH-1000XM5', 'Accessories', 399.99); INSERT INTO product (name, category, price) VALUES ('Apple Watch Ultra 2', 'Wearables', 999.00); INSERT INTO product (name, category, price) VALUES ('Logitech MX Master 3S', 'Accessories', 149.99);

Step 9: Application Properties

application.properties spring.datasource.url=jdbc:h2:mem:auditdb spring.datasource.driver-class-name=org.h2.Driver spring.datasource.username=sa spring.datasource.password= #spring.jpa.hibernate.ddl-auto=update spring.h2.console.enabled=true spring.h2.console.path=/h2-console logging.level.org.springframework.web=INFO spring.security.user.name=admin spring.security.user.password=admin123 # ============= JPA / Hibernate ============= spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.defer-datasource-initialization=true # ============= SQL Initialization ============= spring.sql.init.mode=always

Running the Application

Step 1 — Start the app

Run As → Java Application

Step 2 — Send requests

  • Create a product

curl -X POST http://localhost:8080/api/products \      -H "Content-Type: application/json" \      -d '{"name":"Laptop","category":"Electronics","price":1200}'

  • Fetch all products

curl http://localhost:8080/api/products

  • Update a product

curl -X PUT http://localhost:8080/api/products/1 \      -H "Content-Type: application/json" \      -d '{"name":"Laptop Pro","category":"Electronics","price":1350}'

  • Delete a product

curl -X DELETE http://localhost:8080/api/products/1

Outputs and Explanations

Console Log Output

2025-11-02T23:37:47.372+05:30 INFO 9392 --- [nio-8080-exec-7] c.e.auditlogging.aspect.AuditAspect : ✅ Audit log saved successfully for method: getAllProducts 2025-11-02T23:38:47.591+05:30 INFO 9392 --- [nio-8080-exec-6] c.e.auditlogging.aspect.AuditAspect : ✅ Audit log saved successfully for method: createProduct 2025-11-02T23:39:20.194+05:30 INFO 9392 --- [nio-8080-exec-9] c.e.auditlogging.aspect.AuditAspect : ✅ Audit log saved successfully for method: deleteProduct

Console Log

Explanation

Each line indicates the method name captured by the audit aspect and successful persistence of its record.

Database Table: AUDIT_LOG

| ID | TIMESTAMP | DETAILS | ACTION | USERNAME | |----|----|----|----|----| | 1 | 2025-11-02 23:37:47.27367 | Method getAllProducts executed with args [] | GETALLPRODUCTS | system | | 2 | 2025-11-02 23:38:47.59111 | Method createProduct executed with args [com.example.auditlogging.entity.Product@37a1b59c] | CREATEPRODUCT | system | | 3 | 2025-11-02 23:39:20.194292 | Method deleteProduct executed with args [2] | DELETEPRODUCT | system |

HTTP Response Example

Request

Response

{ "id": 8, "name": "MacBook Air", "category": "Laptop", "price": 1199.99 }

Response Headers

Content-Type: application/json X-Correlation-Id: 8a41dc7e-f3a9-4b78-9f10-8c239e62a4f4

Explanation:

The response returns the saved product details along with the correlation ID generated by the filter.

Audit Entry for Above Request

| ID | TIMESTAMP | DETAIL | ACTION | USERNAME | |----|----|----|----|----| | 1 | 2025-11-02 23:38:47.59111 | Method createProduct executed with args [com.example.auditlogging.entity.Product@37a1b59c] | CREATEPRODUCT | system |

Combined Flow Visualization

| Step | Component | What Happens | Example Output | |----|----|----|----| | 1 | Controller | POST /api/products executes | Product created | | 2 | Aspect | Captures method name + args | CREATEPRODUCT | | 3 | Repository | Saves AuditLog entry | Row inserted in DB | | 4 | Logger | Prints success message | Audit log saved successfully… | | 5 | Filter | Adds correlation ID to response | X-Correlation-Id: |

Final Output Summary

After running all four operations (Create, Read, Update, Delete):

Console Output

Audit log saved successfully for method: createProduct Audit log saved successfully for method: getAllProducts Audit log saved successfully for method: updateProduct Audit log saved successfully for method: deleteProduct

Database

Four rows in AUDIT_LOG table representing each action.

Response Header

Each API response includes X-Correlation-Id.

Response Body Example

{ "id": 1, "name": "Laptop Pro", "category": "Electronics", "price": 1350.0 }

Extending It for Real Users

You can easily integrate with Spring Security to capture the actual logged-in username:

String username = SecurityContextHolder.getContext().getAuthentication().getName(); log.setUsername(username);

Conclusion

You now have a fully working audit logging framework in Spring Boot that automatically captures all REST API actions with minimal code.

This approach ensures:

  • Centralized audit logging for all REST endpoints
  • Non-intrusive — no need to modify each controller
  • Easily extendable to capture IP address, headers, or request body
  • Ready for production with async logging and correlation IDs

This setup ensures every REST API call leaves a clear trace for debugging and compliance purposes.

\

Piyasa Fırsatı
Ark of Panda Logosu
Ark of Panda Fiyatı(AOP)
$0,03267
$0,03267$0,03267
+%2,12
USD
Ark of Panda (AOP) Canlı Fiyat Grafiği
Sorumluluk Reddi: Bu sitede yeniden yayınlanan makaleler, halka açık platformlardan alınmıştır ve yalnızca bilgilendirme amaçlıdır. MEXC'nin görüşlerini yansıtmayabilir. Tüm hakları telif sahiplerine aittir. Herhangi bir içeriğin üçüncü taraf haklarını ihlal ettiğini düşünüyorsanız, kaldırılması için lütfen service@support.mexc.com ile iletişime geçin. MEXC, içeriğin doğruluğu, eksiksizliği veya güncelliği konusunda hiçbir garanti vermez ve sağlanan bilgilere dayalı olarak alınan herhangi bir eylemden sorumlu değildir. İçerik, finansal, yasal veya diğer profesyonel tavsiye niteliğinde değildir ve MEXC tarafından bir tavsiye veya onay olarak değerlendirilmemelidir.

Ayrıca Şunları da Beğenebilirsiniz

The Role of Blockchain in Building Safer Web3 Gaming Ecosystems

The Role of Blockchain in Building Safer Web3 Gaming Ecosystems

The gaming industry is in the midst of a historic shift, driven by the rise of Web3. Unlike traditional games, where developers and publishers control assets and dictate in-game economies, Web3 gaming empowers players with ownership and influence. Built on blockchain technology, these ecosystems are decentralized by design, enabling true digital asset ownership, transparent economies, and a future where players help shape the games they play. However, as Web3 gaming grows, security becomes a focal point. The range of security concerns, from hacking to asset theft to vulnerabilities in smart contracts, is a significant issue that will undermine or erode trust in this ecosystem, limiting or stopping adoption. Blockchain technology could be used to create security processes around secure, transparent, and fair Web3 gaming ecosystems. We will explore how security is increasing within gaming ecosystems, which challenges are being overcome, and what the future of security looks like. Why is Security Important in Web3 Gaming? Web3 gaming differs from traditional gaming in that players engage with both the game and assets with real value attached. Players own in-game assets that exist as tokens or NFTs (Non-Fungible Tokens), and can trade and sell them. These game assets usually represent significant financial value, meaning security failure could represent real monetary loss. In essence, without security, the promises of owning “something” in Web3, decentralized economies within games, and all that comes with the term “fair” gameplay can easily be eroded by fraud, hacking, and exploitation. This is precisely why the uniqueness of blockchain should be emphasized in securing Web3 gaming. How Blockchain Ensures Security in Web3 Gaming?
  1. Immutable Ownership of Assets Blockchain records can be manipulated by anyone. If a player owns a sword, skin, or plot of land as an NFT, it is verifiably in their ownership, and it cannot be altered or deleted by the developer or even hacked. This has created a proven track record of ownership, providing control back to the players, unlike any centralised gaming platform where assets can be revoked.
  2. Decentralized Infrastructure Blockchain networks also have a distributed architecture where game data is stored in a worldwide network of nodes, making them much less susceptible to centralised points of failure and attacks. This decentralised approach makes it exponentially more difficult to hijack systems or even shut off the game’s economy.
  3. Secure Transactions with Cryptography Whether a player buys an NFT or trades their in-game tokens for other items or tokens, the transactions are enforced by cryptographic algorithms, ensuring secure, verifiable, and irreversible transactions and eliminating the risks of double-spending or fraudulent trades.
  4. Smart Contract Automation Smart contracts automate the enforcement of game rules and players’ economic exchanges for the developer, eliminating the need for intermediaries or middlemen, and trust for the developer. For example, if a player completes a quest that promises a reward, the smart contract will execute and distribute what was promised.
  5. Anti-Cheating and Fair Gameplay The naturally transparent nature of blockchain makes it extremely simple for anyone to examine a specific instance of gameplay and verify the economic outcomes from that play. Furthermore, multi-player games that enforce smart contracts on things like loot sharing or win sharing can automate and measure trustlessness and avoid cheating, manipulations, and fraud by developers.
  6. Cross-Platform Security Many Web3 games feature asset interoperability across platforms. This interoperability is made viable by blockchain, which guarantees ownership is maintained whenever assets transition from one game or marketplace to another, thereby offering protection to players who rely on transfers for security against fraud. Key Security Dangers in Web3 Gaming Although blockchain provides sound first principles of security, the Web3 gaming ecosystem is susceptible to threats. Some of the most serious threats include:
Smart Contract Vulnerabilities: Smart contracts that are poorly written or lack auditing will leave openings for exploitation and thereby result in asset loss. Phishing Attacks: Unintentionally exposing or revealing private keys or signing transactions that are not possible to reverse, under the assumption they were genuine transaction requests. Bridge Hacks: Cross-chain bridges, which allow players to move their assets between their respective blockchains, continually face hacks, requiring vigilance from players and developers. Scams and Rug Pulls: Rug pulls occur when a game project raises money and leaves, leaving player assets worthless. Regulatory Ambiguity: Global regulations remain unclear; risks exist for players and developers alike. While blockchain alone won’t resolve every issue, it remediates the responsibility of the first principles, more so when joined by processes such as auditing, education, and the right governance, which can improve their contribution to the security landscapes in game ecosystems. Real Life Examples of Blockchain Security in Web3 Gaming Axie Infinity (Ronin Hack): The Axie Infinity game and several projects suffered one of the biggest hacks thus far on its Ronin bridge; however, it demonstrated the effectiveness of multi-sig security and the effective utilization of decentralization. The industry benefited through learning and reflection, thus, as projects have implemented changes to reduce the risks of future hacks or misappropriation. Immutable X: This Ethereum scaling solution aims to ensure secure NFT transactions for gaming, allowing players to trade an asset without the burden of exorbitant fees and fears of being a victim of fraud. Enjin: Enjin is providing a trusted infrastructure for Web3 games, offering secure NFT creation and transfer while reiterating that ownership and an asset securely belong to the player. These examples indubitably illustrate that despite challenges to overcome, blockchain remains the foundational layer on which to build more secure Web3 gaming environments. Benefits of Blockchain Security for Players and Developers For Players: Confidence in true ownership of assets Transparency in in-game economies Protection against nefarious trades/scams For Developers: More trust between players and the platform Less reliance on centralized infrastructure Ability to attract wealth and players based on provable fairness By incorporating blockchain security within the mechanics of game design, developers can create and enforce resilient ecosystems where players feel reassured in investing time, money, and ownership within virtual worlds. The Future of Secure Web3 Gaming Ecosystems As the wisdom of blockchain technology and industry knowledge improves, the future for secure Web3 gaming looks bright. New growing trends include: Zero-Knowledge Proofs (ZKPs): A new wave of protocols that enable private transactions and secure smart contracts while managing user privacy with an element of transparency. Decentralized Identity Solutions (DID): Helping players control their identities and decrease account theft risks. AI-Enhanced Security: Identifying irregularities in user interactions by sampling pattern anomalies to avert hacks and fraud by time-stamping critical events. Interoperable Security Standards: Allowing secured and seamless asset transfers across blockchains and games. With these innovations, blockchain will not only secure gaming assets but also enhance the overall trust and longevity of Web3 gaming ecosystems. Conclusion Blockchain is more than a buzzword in Web3; it is the only way to host security, fairness, and transparency. With blockchain, players confirm immutable ownership of digital assets, there is a decentralized infrastructure, and finally, it supports smart contracts to automate code that protects players and developers from the challenges of digital economies. The threats, vulnerabilities, and scams that come from smart contracts still persist, but the industry is maturing with better security practices, cross-chain solutions, and increased formal cryptographic tools. In the coming years, blockchain will remain the base to digital economies and drive Web3 gaming environments that allow players to safely own, trade, and enjoy their digital experiences free from fraud and exploitation. While blockchain and gaming alone entertain, we will usher in an era of secure digital worlds where trust complements innovation. The Role of Blockchain in Building Safer Web3 Gaming Ecosystems was originally published in Coinmonks on Medium, where people are continuing the conversation by highlighting and responding to this story
Paylaş
Medium2025/09/18 14:40
Knocking Bitcoin's lack of yield shows your ‘Western financial privilege’

Knocking Bitcoin's lack of yield shows your ‘Western financial privilege’

                                                                               Macro analyst Luke Gromen’s comments come amid an ongoing debate over whether Bitcoin or Ether is the more attractive long-term option for traditional investors.                     Macro analyst Luke Gromen says the fact that Bitcoin doesn’t natively earn yield isn’t a weakness; it’s what makes it a safer store of value.“If you’re earning a yield, you are taking a risk,” Gromen told Natalie Brunell on the Coin Stories podcast on Wednesday, responding to a question about critics who dismiss Bitcoin (BTC) because they prefer yield-earning assets.“Anyone who says that is showing their Western financial privilege,” he added.Read more
Paylaş
Coinstats2025/09/18 14:22
Vitalik Buterin wants to build ‘the next generation of finance’ – Here’s how

Vitalik Buterin wants to build ‘the next generation of finance’ – Here’s how

The post Vitalik Buterin wants to build ‘the next generation of finance’ – Here’s how appeared on BitcoinEthereumNews.com. Journalist Posted: February 16, 2026
Paylaş
BitcoinEthereumNews2026/02/16 11:01