Solidity GraphQL Extension for Native G-Chain Integration
Extends Solidity with embedded GraphQL syntax for seamless cross-chain queries from C-Chain smart contracts
See also: LP-1200: C-Chain EVM Specification, LP-1226: C-Chain EVM Equivalence, LP-0098: GraphDB & GraphQL Integration
Abstract
This LP introduces a Solidity language extension that embeds GraphQL query syntax directly into smart contracts, enabling native integration with G-Chain from C-Chain contracts. The extension includes:
- GraphQL query literals with compile-time validation
- Automatic code generation for type-safe query results
- Gas-efficient precompile calls to G-Chain
- Built-in caching and result pagination
- Quantum-safe query authentication
This allows developers to write cross-chain queries as naturally as they write regular Solidity code, dramatically simplifying dApp development across Lux's 8-chain architecture.
Motivation
Current cross-chain data access from smart contracts is complex and error-prone:
- Manual ABI encoding/decoding for cross-chain calls
- No compile-time validation of queries
- Type safety lost between query and result handling
- High gas costs for complex data retrieval
- No standard patterns for caching or pagination
By embedding GraphQL directly in Solidity, we can:
- Validate queries at compile time
- Generate type-safe result structs automatically
- Optimize gas usage through precompiles
- Provide intuitive syntax familiar to web developers
- Enable complex cross-chain queries with minimal code
Specification
GraphQL Query Syntax
Introduce a new query keyword for GraphQL literals:
pragma solidity ^0.8.24;
pragma experimental GraphQL;
contract DeFiAggregator {
// GraphQL query defined at contract level
query GetUserPositions {
"""
query UserPortfolio($user: Address!, $chains: [ChainID!]) {
positions(owner: $user, chains: $chains) {
chain
protocol
asset {
symbol
decimals
address
}
amount
valueUSD
}
totalValueUSD: aggregate(fn: SUM, field: valueUSD)
}
"""
}
// Auto-generated struct from query
struct UserPortfolioResult {
Position[] positions;
uint256 totalValueUSD;
}
struct Position {
string chain;
string protocol;
Asset asset;
uint256 amount;
uint256 valueUSD;
}
struct Asset {
string symbol;
uint8 decimals;
address address;
}
}
Rationale
- Embedding GraphQL enables compile‑time validation and type safety, reducing cross‑chain query bugs.
- A precompile abstracts transport details, allowing gas‑efficient execution without bespoke bridges.
- Familiar GraphQL syntax lowers the learning curve for web developers building Lux dApps.
Query Execution
Queries are executed through a special precompile at 0x0100:
function getUserPortfolio(address user) external view returns (UserPortfolioResult memory) {
// Compiler generates this call
bytes memory queryData = abi.encode(
GetUserPositions.selector,
user,
["C", "X", "A"] // chains parameter
);
(bool success, bytes memory result) = address(0x0100).staticcall(queryData);
require(success, "GraphQL query failed");
return abi.decode(result, (UserPortfolioResult));
}
Inline Queries
Support inline queries for dynamic use cases:
function getTokenPrice(address token) external view returns (uint256) {
// Inline query with automatic type inference
var result = query {
"""
query TokenPrice($token: Address!) {
token(address: $token) {
priceUSD
liquidity
volume24h
}
}
"""
} with { token: token };
return result.token.priceUSD;
}
Query Modifiers
Support common GraphQL operations:
// Pagination
query GetTransactions {
"""
query RecentTxs($user: Address!, $limit: Int = 10, $offset: Int = 0) {
transactions(from: $user, limit: $limit, offset: $offset) @paginate {
hash
to
value
timestamp
}
}
"""
}
// Caching
query GetStats {
"""
query ProtocolStats @cache(ttl: 300) {
stats {
tvl
users
transactions
}
}
"""
}
// Real-time subscriptions (for off-chain watchers)
query WatchPrices {
"""
subscription PriceUpdates($pairs: [String!]) @realtime {
priceUpdate(pairs: $pairs) {
pair
price
timestamp
}
}
"""
}
Cross-Chain Aggregation
Enable complex cross-chain queries:
query GetCrossChainBalance {
"""
query TotalBalance($user: Address!, $token: String!) {
chains {
id
name
balance(owner: $user, symbol: $token) {
amount
valueUSD
}
}
total: aggregate(
source: chains.balance.valueUSD,
fn: SUM
)
}
"""
}
function getTotalUSDValue(address user, string memory token)
external view returns (uint256)
{
var result = GetCrossChainBalance.execute(user, token);
return result.total;
}
Gas Optimization
The compiler optimizes GraphQL queries:
- Query Deduplication: Identical queries share bytecode
- Result Caching: Built-in result caching with TTL
- Batch Queries: Multiple queries in single precompile call
- Lazy Loading: Only requested fields are returned
// Batch multiple queries
function getDefiStats(address user) external view {
var results = query.batch {
positions: GetUserPositions(user, ["C", "X"]),
prices: GetTokenPrices(["LUX", "ETH", "USDC"]),
apr: GetPoolAPR(user.activePools)
};
// Process results...
}
Type Safety
The compiler ensures type safety:
// Compile error: field doesn't exist
var price = result.token.notAField; // Error!
// Compile error: type mismatch
uint256 symbol = result.token.symbol; // Error: string to uint256
// Automatic type coercion where safe
uint256 amount = result.balance; // OK if balance is numeric
Security Features
All queries are quantum-safe:
modifier onlyWithAttestation() {
// Queries automatically include attestation
require(msg.sender.hasValidAttestation(), "Invalid attestation");
_;
}
function secureQuery() external view onlyWithAttestation {
// Query includes dual-certificate signature
var result = query {
"""
query SecureData @authenticated {
sensitiveData {
value
proof
}
}
"""
};
}
Implementation
Compiler Extensions
- Parser: Recognize
querykeyword and GraphQL syntax - Validator: Validate GraphQL syntax at compile time
- Type Generator: Generate Solidity structs from GraphQL schema
- Code Generator: Generate efficient precompile calls
- Optimizer: Deduplicate queries and optimize gas usage
Runtime Support
- Precompile 0x0100: G-Chain query executor
- Result Cache: In-memory cache for query results
- Schema Registry: On-chain GraphQL schema storage
- Query Validator: Runtime query validation
Development Tools
- IDE Support: Syntax highlighting and autocomplete
- Schema Generator: Generate GraphQL schema from contracts
- Query Builder: Visual query builder for Solidity
- Gas Estimator: Estimate query gas costs
- Testing Framework: Mock G-Chain responses
Example Use Cases
DeFi Aggregator
contract DeFiAggregator {
query FindBestRate {
"""
query BestSwapRate($tokenIn: String!, $tokenOut: String!, $amount: BigInt!) {
dexes {
protocol
rate(from: $tokenIn, to: $tokenOut, amount: $amount)
liquidity
fee
}
best: max(source: dexes.rate)
}
"""
}
function swap(address tokenIn, address tokenOut, uint256 amount) external {
var result = FindBestRate.execute(tokenIn.symbol(), tokenOut.symbol(), amount);
IDex(result.best.protocol).swap(tokenIn, tokenOut, amount);
}
}
NFT Marketplace
contract NFTMarketplace {
query GetNFTHistory {
"""
query NFTProvenance($collection: Address!, $tokenId: BigInt!) {
nft(collection: $collection, id: $tokenId) {
creator
owners {
address
acquiredAt
price
}
attributes
metadata
}
}
"""
}
}
Cross-Chain Bridge
contract UniversalBridge {
query CheckBridgeLiquidity {
"""
query BridgeLiquidity($token: String!, $amount: BigInt!) {
chains {
id
name
liquidity(token: $token) {
available
sufficient: gte(available, $amount)
}
}
viableChains: filter(
source: chains,
where: liquidity.sufficient == true
)
}
"""
}
}
Test Cases
1. Basic Query Compilation
// Test: Query compiles and generates correct struct
pragma solidity ^0.8.24;
pragma experimental GraphQL;
contract TestBasicQuery {
query GetBalance {
"""
query Balance($user: Address!) {
account(address: $user) {
balance
}
}
"""
}
function test_queryCompiles() public view {
address user = address(0x1234);
var result = GetBalance.execute(user);
assert(result.account.balance >= 0);
}
}
2. Type Safety Enforcement
// Test: Compiler rejects type mismatches
contract TestTypeSafety {
query GetUser {
"""
query User($id: BigInt!) {
user(id: $id) {
name
age
}
}
"""
}
function test_typeMismatchFails() public {
// This should fail at compile time:
// string memory age = GetUser.execute(1).user.age; // Error: uint to string
// This should succeed:
uint256 age = GetUser.execute(1).user.age;
string memory name = GetUser.execute(1).user.name;
}
}
3. Cross-Chain Aggregation
// Test: Multi-chain query returns aggregated results
contract TestCrossChain {
query GetMultiChainBalance {
"""
query TotalBalance($user: Address!) {
chains {
id
balance(owner: $user)
}
total: aggregate(source: chains.balance, fn: SUM)
}
"""
}
function test_crossChainAggregation() public view {
address user = msg.sender;
var result = GetMultiChainBalance.execute(user);
// Verify all chains returned
assert(result.chains.length == 8); // All 8 Lux chains
// Verify aggregation
uint256 sum = 0;
for (uint i = 0; i < result.chains.length; i++) {
sum += result.chains[i].balance;
}
assert(result.total == sum);
}
}
4. Caching Behavior
// Test: Cached queries return consistent results
contract TestCaching {
query GetCachedData {
"""
query Stats @cache(ttl: 300) {
stats {
tvl
timestamp
}
}
"""
}
function test_cacheHit() public view {
var result1 = GetCachedData.execute();
var result2 = GetCachedData.execute();
// Within TTL, results should be identical
assert(result1.stats.timestamp == result2.stats.timestamp);
}
}
5. Gas Metering
// Test: Complex queries respect gas limits
contract TestGasLimits {
query GetLargeDataset {
"""
query AllTransactions($limit: Int!) {
transactions(limit: $limit) {
hash
from
to
value
}
}
"""
}
function test_gasLimitEnforced() public view {
uint256 gasBefore = gasleft();
var result = GetLargeDataset.execute(1000);
uint256 gasUsed = gasBefore - gasleft();
// Verify gas consumption is within expected bounds
assert(gasUsed < 500000); // Max 500k gas for query
assert(result.transactions.length <= 1000);
}
}
6. Precompile Integration
// Test: Direct precompile call returns valid response
contract TestPrecompile {
address constant G_CHAIN_PRECOMPILE = address(0x0100);
function test_precompileCall() public view {
bytes memory queryData = abi.encode(
bytes4(keccak256("ping()")),
""
);
(bool success, bytes memory result) = G_CHAIN_PRECOMPILE.staticcall(queryData);
assert(success);
assert(result.length > 0);
}
}
7. Error Handling
// Test: Invalid queries revert with descriptive errors
contract TestErrorHandling {
query GetInvalidField {
"""
query InvalidQuery {
nonexistent {
field
}
}
"""
}
function test_invalidQueryReverts() public view {
// Should revert with "GraphQL: unknown field 'nonexistent'"
try this.executeInvalid() {
revert("Should have reverted");
} catch Error(string memory reason) {
assert(bytes(reason).length > 0);
}
}
function executeInvalid() external view {
GetInvalidField.execute();
}
}
8. Batch Query Execution
// Test: Batch queries execute atomically
contract TestBatchQueries {
function test_batchExecution() public view {
var results = query.batch {
balance: GetBalance(msg.sender),
prices: GetPrices(["LUX", "ETH"]),
stats: GetProtocolStats()
};
// All queries should succeed or all should fail
assert(results.balance.account.balance >= 0);
assert(results.prices.length == 2);
assert(results.stats.tvl > 0);
}
}
Backwards Compatibility
This extension is opt-in via pragma experimental GraphQL. Contracts without this pragma are unaffected. The feature can be enabled per-contract, allowing gradual adoption.
Security Considerations
- Query Injection: Queries are parsed at compile-time, preventing injection
- Gas Limits: Queries respect block gas limits via precompile metering
- Access Control: Queries inherit contract's access control
- Result Validation: Results are validated against schema
- Quantum Safety: All queries use dual-certificate authentication
Future Enhancements
- Query Composition: Reuse query fragments
- Schema Evolution: Automatic migration for schema changes
- Off-chain Execution: Execute queries in view functions
- Query Optimization: AI-powered query optimization
- Cross-Contract Queries: Share queries between contracts
Conclusion
By embedding GraphQL directly into Solidity, we dramatically simplify cross-chain dApp development on Lux. Developers can write complex queries with the same ease as calling a local function, while the compiler handles all the complexity of cross-chain communication, type safety, and gas optimization.
Copyright
Copyright and related rights waived via CC0.