Skip to main content

nectar_contracts/
lib.rs

1//! Swarm contract bindings and deployment information.
2//!
3//! This crate provides type-safe Solidity contract bindings using Alloy's `sol!` macro,
4//! along with deployment information for mainnet and testnet.
5//!
6//! # Deployment Information
7//!
8//! Each contract has a deployment struct that bundles address and deployment block:
9//!
10//! ```
11//! use nectar_contracts::mainnet;
12//!
13//! let postage = mainnet::POSTAGE_STAMP;
14//! assert_ne!(postage.address, alloy_primitives::Address::ZERO);
15//! assert!(postage.block > 0);
16//! ```
17//!
18//! # Contract Bindings
19//!
20//! The `sol!` macro generates call types, return types, and event types that can be
21//! used with alloy providers:
22//!
23//! ```ignore
24//! use alloy_sol_types::SolCall;
25//! use nectar_contracts::{IPostageStamp, mainnet};
26//!
27//! // Encode a call
28//! let call = IPostageStamp::batchOwnerCall { batchId: batch_id };
29//! let encoded = call.abi_encode();
30//! ```
31
32#![cfg_attr(not(test), warn(unused_crate_dependencies))]
33#![cfg_attr(not(feature = "std"), no_std)]
34#![cfg_attr(docsrs, feature(doc_cfg))]
35
36use alloy_primitives::{Address, address};
37use alloy_sol_types::sol;
38
39// Deployment Info Macro
40
41/// Macro to define a contract deployment struct with address and block.
42macro_rules! define_deployment {
43    ($(#[$meta:meta])* $name:ident) => {
44        $(#[$meta])*
45        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
46        pub struct $name {
47            /// Contract address.
48            pub address: Address,
49            /// Deployment block number.
50            pub block: u64,
51        }
52
53        impl $name {
54            /// Creates a new deployment.
55            #[must_use]
56            pub const fn new(address: Address, block: u64) -> Self {
57                Self { address, block }
58            }
59        }
60    };
61}
62
63// Deployment Information Types
64
65define_deployment!(
66    /// BZZ token deployment information.
67    Token
68);
69
70define_deployment!(
71    /// Postage stamp contract deployment information.
72    PostageStamp
73);
74
75define_deployment!(
76    /// Stake registry contract deployment information.
77    StakeRegistry
78);
79
80define_deployment!(
81    /// Redistribution contract deployment information.
82    Redistribution
83);
84
85define_deployment!(
86    /// Storage price oracle contract deployment information.
87    StoragePriceOracle
88);
89
90define_deployment!(
91    /// Chequebook factory contract deployment information.
92    ChequebookFactory
93);
94
95define_deployment!(
96    /// Swap price oracle contract deployment information.
97    SwapPriceOracle
98);
99
100// Token Interface
101
102sol! {
103    /// Standard ERC20 token interface.
104    #[derive(Debug, PartialEq, Eq)]
105    interface IERC20 {
106        function name() external view returns (string memory);
107        function symbol() external view returns (string memory);
108        function decimals() external view returns (uint8);
109        function totalSupply() external view returns (uint256);
110        function balanceOf(address account) external view returns (uint256);
111        function transfer(address to, uint256 amount) external returns (bool);
112        function allowance(address owner, address spender) external view returns (uint256);
113        function approve(address spender, uint256 amount) external returns (bool);
114        function transferFrom(address from, address to, uint256 amount) external returns (bool);
115
116        event Transfer(address indexed from, address indexed to, uint256 value);
117        event Approval(address indexed owner, address indexed spender, uint256 value);
118    }
119}
120
121// Storage Incentive Contract Interfaces
122
123sol! {
124    /// Postage stamp contract interface.
125    ///
126    /// Manages postage stamp batches required for uploading data to Swarm.
127    #[derive(Debug, PartialEq, Eq)]
128    interface IPostageStamp {
129        function withdraw(address beneficiary) external;
130        function setPrice(uint256 price) external;
131        function validChunkCount() external view returns (uint256);
132        function batchOwner(bytes32 batchId) external view returns (address);
133        function batchDepth(bytes32 batchId) external view returns (uint8);
134        function batchBucketDepth(bytes32 batchId) external view returns (uint8);
135        function remainingBalance(bytes32 batchId) external view returns (uint256);
136        function minimumInitialBalancePerChunk() external view returns (uint256);
137        function batches(bytes32 batchId) external view returns (
138            address owner,
139            uint8 depth,
140            uint8 bucketDepth,
141            bool immutableFlag,
142            uint256 normalisedBalance,
143            uint256 lastUpdatedBlockNumber
144        );
145    }
146
147    /// Stake registry contract interface.
148    ///
149    /// Manages staking for nodes participating in storage incentives.
150    #[derive(Debug, PartialEq, Eq)]
151    interface IStakeRegistry {
152        function stakes(address owner) external view returns (
153            bytes32 overlay,
154            uint256 committedStake,
155            uint256 potentialStake,
156            uint256 lastUpdatedBlockNumber,
157            uint8 height
158        );
159        function overlayOfAddress(address owner) external view returns (bytes32);
160        function heightOfAddress(address owner) external view returns (uint8);
161        function nodeEffectiveStake(address owner) external view returns (uint256);
162        function lastUpdatedBlockNumberOfAddress(address owner) external view returns (uint256);
163        function freezeDeposit(address owner, uint256 time) external;
164
165        event StakeUpdated(
166            address indexed owner,
167            uint256 committedStake,
168            uint256 potentialStake,
169            bytes32 overlay,
170            uint256 lastUpdatedBlock,
171            uint8 height
172        );
173        event StakeSlashed(address slashed, bytes32 overlay, uint256 amount);
174        event StakeFrozen(address frozen, bytes32 overlay, uint256 time);
175        event StakeWithdrawn(address node, uint256 amount);
176    }
177
178    /// Redistribution contract interface.
179    ///
180    /// Implements the Schelling coordination game for storage reward distribution.
181    #[derive(Debug, PartialEq, Eq)]
182    interface IRedistribution {
183        function currentRound() external view returns (uint64);
184        function currentPhaseCommit() external view returns (bool);
185        function currentPhaseReveal() external view returns (bool);
186        function currentPhaseClaim() external view returns (bool);
187        function isParticipatingInUpcomingRound(bytes32 overlay, uint8 depth) external view returns (bool);
188        function isWinner(bytes32 overlay) external view returns (bool);
189        function claim(
190            bytes32[] calldata proofSegments,
191            bytes32 proveSegment,
192            bytes32[] calldata proofSegments2,
193            bytes32 proveSegment2,
194            uint64 chunkSpan,
195            bytes32[] calldata proofSegments3
196        ) external;
197    }
198
199    /// Storage price oracle contract interface.
200    ///
201    /// Controls the price per chunk for postage stamp batches.
202    #[derive(Debug, PartialEq, Eq)]
203    interface IStoragePriceOracle {
204        function PRICE_UPDATER_ROLE() external view returns (uint256);
205        function postageStamp() external view returns (address);
206        function currentPrice() external view returns (uint32);
207        function minimumPrice() external view returns (uint32);
208        function currentRound() external view returns (uint64);
209        function lastAdjustedRound() external view returns (uint64);
210        function isPaused() external view returns (bool);
211        function setPrice(uint32 price) external returns (bool);
212        function adjustPrice(uint16 redundancy) external returns (bool);
213        function pause() external;
214        function unPause() external;
215
216        event PriceUpdate(uint256 price);
217        event StampPriceUpdateFailed(uint256 attemptedPrice);
218    }
219}
220
221// Swap Contract Interfaces (Chequebook)
222
223#[cfg(feature = "serde")]
224sol! {
225    /// EIP-712 cheque struct for chequebook payments.
226    ///
227    /// This is the typed data structure used for signing cheques off-chain.
228    /// The EIP-712 domain uses:
229    /// - Name: "Chequebook"
230    /// - Version: "1.0"
231    /// - ChainId: network-specific (100 for Gnosis, 11155111 for Sepolia)
232    #[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
233    struct Cheque {
234        address chequebook;
235        address beneficiary;
236        uint256 cumulativePayout;
237    }
238}
239
240#[cfg(not(feature = "serde"))]
241sol! {
242    /// EIP-712 cheque struct for chequebook payments.
243    ///
244    /// This is the typed data structure used for signing cheques off-chain.
245    /// The EIP-712 domain uses:
246    /// - Name: "Chequebook"
247    /// - Version: "1.0"
248    /// - ChainId: network-specific (100 for Gnosis, 11155111 for Sepolia)
249    #[derive(Debug, PartialEq, Eq)]
250    struct Cheque {
251        address chequebook;
252        address beneficiary;
253        uint256 cumulativePayout;
254    }
255}
256
257sol! {
258    /// Chequebook contract interface (ERC20SimpleSwap).
259    ///
260    /// Allows the issuer to send cheques to counterparties for peer-to-peer payments.
261    #[derive(Debug, PartialEq, Eq)]
262    interface IChequebook {
263        function issuer() external view returns (address);
264        function token() external view returns (address);
265        function balance() external view returns (uint256);
266        function liquidBalance() external view returns (uint256);
267        function liquidBalanceFor(address beneficiary) external view returns (uint256);
268        function paidOut(address beneficiary) external view returns (uint256);
269        function totalPaidOut() external view returns (uint256);
270        function totalHardDeposit() external view returns (uint256);
271        function defaultHardDepositTimeout() external view returns (uint256);
272        function bounced() external view returns (bool);
273        function hardDeposits(address beneficiary) external view returns (
274            uint256 amount,
275            uint256 decreaseAmount,
276            uint256 timeout,
277            uint256 canBeDecreasedAt
278        );
279        function init(address _issuer, address _token, uint256 _defaultHardDepositTimeout) external;
280        function cashCheque(
281            address beneficiary,
282            address recipient,
283            uint256 cumulativePayout,
284            bytes memory beneficiarySig,
285            uint256 callerPayout,
286            bytes memory issuerSig
287        ) external;
288        function cashChequeBeneficiary(address recipient, uint256 cumulativePayout, bytes memory issuerSig) external;
289        function increaseHardDeposit(address beneficiary, uint256 amount) external;
290        function prepareDecreaseHardDeposit(address beneficiary, uint256 decreaseAmount) external;
291        function decreaseHardDeposit(address beneficiary) external;
292        function setCustomHardDepositTimeout(address beneficiary, uint256 hardDepositTimeout, bytes memory beneficiarySig) external;
293        function withdraw(uint256 amount) external;
294
295        event ChequeCashed(
296            address indexed beneficiary,
297            address indexed recipient,
298            address indexed caller,
299            uint256 totalPayout,
300            uint256 cumulativePayout,
301            uint256 callerPayout
302        );
303        event ChequeBounced();
304        event HardDepositAmountChanged(address indexed beneficiary, uint256 amount);
305        event HardDepositDecreasePrepared(address indexed beneficiary, uint256 decreaseAmount);
306        event HardDepositTimeoutChanged(address indexed beneficiary, uint256 timeout);
307        event Withdraw(uint256 amount);
308    }
309
310    /// Chequebook factory contract interface (SimpleSwapFactory).
311    #[derive(Debug, PartialEq, Eq)]
312    interface IChequebookFactory {
313        function ERC20Address() external view returns (address);
314        function master() external view returns (address);
315        function deployedContracts(address addr) external view returns (bool);
316        function deploySimpleSwap(address issuer, uint256 defaultHardDepositTimeoutDuration, bytes32 salt) external returns (address);
317
318        event SimpleSwapDeployed(address contractAddress);
319    }
320
321    /// Swap price oracle contract interface.
322    #[derive(Debug, PartialEq, Eq)]
323    interface ISwapPriceOracle {
324        function price() external view returns (uint256);
325        function chequeValueDeduction() external view returns (uint256);
326        function getPrice() external view returns (uint256 price, uint256 chequeValueDeduction);
327        function updatePrice(uint256 newPrice) external;
328        function updateChequeValueDeduction(uint256 newChequeValueDeduction) external;
329
330        event PriceUpdate(uint256 price);
331        event ChequeValueDeductionUpdate(uint256 chequeValueDeduction);
332    }
333}
334
335// Gnosis Chain Mainnet Deployments
336
337/// Gnosis Chain mainnet contract deployments.
338pub mod mainnet {
339    use super::*;
340
341    /// BZZ token (xBZZ on Gnosis Chain).
342    pub const BZZ_TOKEN: Token =
343        Token::new(address!("dBF3Ea6F5beE45c02255B2c26a16F300502F68da"), 0);
344
345    /// Postage stamp contract.
346    pub const POSTAGE_STAMP: PostageStamp = PostageStamp::new(
347        address!("5b53f7a1975eb212d4b20b7cdd443baa189af7c9"),
348        31305656,
349    );
350
351    /// Stake registry contract.
352    pub const STAKING: StakeRegistry = StakeRegistry::new(
353        address!("0c6aa197271466f0afe3818ca03ac47d8f5c2f8a"),
354        40430237,
355    );
356
357    /// Redistribution contract.
358    pub const REDISTRIBUTION: Redistribution = Redistribution::new(
359        address!("eb210c2e166f61b3fd32246d53893f8b9d2a624c"),
360        41105199,
361    );
362
363    /// Storage price oracle contract.
364    pub const STORAGE_PRICE_ORACLE: StoragePriceOracle = StoragePriceOracle::new(
365        address!("47EeF336e7fE5bED98499A4696bce8f28c1B0a8b"),
366        37339168,
367    );
368
369    /// Chequebook factory contract.
370    pub const CHEQUEBOOK_FACTORY: ChequebookFactory = ChequebookFactory::new(
371        address!("c2d5a532cf69aa9a1378737d8ccdef884b6e7420"),
372        39939970,
373    );
374
375    /// Swap price oracle contract.
376    pub const SWAP_PRICE_ORACLE: SwapPriceOracle = SwapPriceOracle::new(
377        address!("A57A50a831B31c904A770edBCb706E03afCdbd94"),
378        39939970,
379    );
380}
381
382// Sepolia Testnet Deployments
383
384/// Sepolia testnet contract deployments.
385pub mod testnet {
386    use super::*;
387
388    /// BZZ token (sBZZ on Sepolia).
389    pub const BZZ_TOKEN: Token =
390        Token::new(address!("6e01ee6183721ae9a006fd4906970c1583863765"), 0);
391
392    /// Postage stamp contract.
393    pub const POSTAGE_STAMP: PostageStamp = PostageStamp::new(
394        address!("621c2e0fa5ed488c7124eb55cc7eb3af75d0d9e8"),
395        6596277,
396    );
397
398    /// Stake registry contract.
399    pub const STAKING: StakeRegistry = StakeRegistry::new(
400        address!("6f252dd6f340f6c6d2f6ee8954b011dd5aba4350"),
401        8262529,
402    );
403
404    /// Redistribution contract.
405    pub const REDISTRIBUTION: Redistribution = Redistribution::new(
406        address!("fb6c7d33be1fb12f4c5da71df7c9d5c22970ba7a"),
407        8646721,
408    );
409
410    /// Storage price oracle contract.
411    pub const STORAGE_PRICE_ORACLE: StoragePriceOracle = StoragePriceOracle::new(
412        address!("95Dc18380e92C13E4F8a4e94C99FB1b97250174B"),
413        8226873,
414    );
415
416    /// Chequebook factory contract.
417    pub const CHEQUEBOOK_FACTORY: ChequebookFactory = ChequebookFactory::new(
418        address!("0fF044F6bB4F684a5A149B46D7eC03ea659F98A1"),
419        4752810,
420    );
421
422    /// Swap price oracle contract.
423    pub const SWAP_PRICE_ORACLE: SwapPriceOracle = SwapPriceOracle::new(
424        address!("1814e9b3951Df0CB8e12b2bB99c5594514588936"),
425        4752810,
426    );
427}
428
429#[cfg(test)]
430mod tests {
431    use super::*;
432    use alloy_primitives::U256;
433
434    #[test]
435    fn test_deployments_non_zero() {
436        // Mainnet
437        assert_ne!(mainnet::BZZ_TOKEN.address, Address::ZERO);
438        assert_ne!(mainnet::POSTAGE_STAMP.address, Address::ZERO);
439        assert_ne!(mainnet::STAKING.address, Address::ZERO);
440        assert_ne!(mainnet::REDISTRIBUTION.address, Address::ZERO);
441        assert_ne!(mainnet::STORAGE_PRICE_ORACLE.address, Address::ZERO);
442        assert_ne!(mainnet::CHEQUEBOOK_FACTORY.address, Address::ZERO);
443        assert_ne!(mainnet::SWAP_PRICE_ORACLE.address, Address::ZERO);
444
445        // Testnet
446        assert_ne!(testnet::BZZ_TOKEN.address, Address::ZERO);
447        assert_ne!(testnet::POSTAGE_STAMP.address, Address::ZERO);
448        assert_ne!(testnet::STAKING.address, Address::ZERO);
449        assert_ne!(testnet::REDISTRIBUTION.address, Address::ZERO);
450        assert_ne!(testnet::STORAGE_PRICE_ORACLE.address, Address::ZERO);
451        assert_ne!(testnet::CHEQUEBOOK_FACTORY.address, Address::ZERO);
452        assert_ne!(testnet::SWAP_PRICE_ORACLE.address, Address::ZERO);
453    }
454
455    #[test]
456    fn test_sol_types_generated() {
457        let _ = IERC20::balanceOfCall {
458            account: Address::ZERO,
459        };
460        let _ = IPostageStamp::batchOwnerCall {
461            batchId: [0u8; 32].into(),
462        };
463        let _ = IStakeRegistry::overlayOfAddressCall {
464            owner: Address::ZERO,
465        };
466        let _ = IChequebookFactory::deploySimpleSwapCall {
467            issuer: Address::ZERO,
468            defaultHardDepositTimeoutDuration: U256::ZERO,
469            salt: [0u8; 32].into(),
470        };
471    }
472}